├── .gitignore ├── Prerender.io.sln ├── Prerender.io ├── Prerender.io.csproj ├── PrerenderConfigSection.cs ├── PrerenderModule.cs ├── Properties │ └── AssemblyInfo.cs ├── ProxyConfigElement.cs ├── ResponseResult.cs └── Utils.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | */bin/* 2 | */obj/* 3 | *.suo 4 | packages/ 5 | .DS_Store 6 | 7 | -------------------------------------------------------------------------------- /Prerender.io.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prerender.io", "Prerender.io\Prerender.io.csproj", "{EF621345-0520-4F7A-80F4-2C04EEAF2D44}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EF621345-0520-4F7A-80F4-2C04EEAF2D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EF621345-0520-4F7A-80F4-2C04EEAF2D44}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EF621345-0520-4F7A-80F4-2C04EEAF2D44}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EF621345-0520-4F7A-80F4-2C04EEAF2D44}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Prerender.io/Prerender.io.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EF621345-0520-4F7A-80F4-2C04EEAF2D44} 8 | Library 9 | Properties 10 | Prerender.io 11 | Prerender.io 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | -------------------------------------------------------------------------------- /Prerender.io/PrerenderConfigSection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | 5 | namespace Prerender.io 6 | { 7 | public sealed class PrerenderConfigSection : ConfigurationSection 8 | { 9 | [ConfigurationProperty("prerenderServiceUrl", DefaultValue = "http://service.prerender.io/")] 10 | public String PrerenderServiceUrl 11 | { 12 | get 13 | { 14 | var prerenderServiceUrl = (String)this["prerenderServiceUrl"]; 15 | return prerenderServiceUrl.IsNotBlank() ? prerenderServiceUrl : "http://service.prerender.io/"; 16 | } 17 | set 18 | { 19 | this["prerenderServiceUrl"] = value; 20 | } 21 | } 22 | 23 | [ConfigurationProperty("stripApplicationNameFromRequestUrl", DefaultValue = false)] 24 | public bool StripApplicationNameFromRequestUrl 25 | { 26 | get 27 | { 28 | return (bool)this["stripApplicationNameFromRequestUrl"]; 29 | } 30 | set 31 | { 32 | this["stripApplicationNameFromRequestUrl"] = value; 33 | } 34 | } 35 | 36 | [ConfigurationProperty("whitelist")] 37 | public String WhitelistString 38 | { 39 | get 40 | { 41 | return (String)this["whitelist"]; 42 | } 43 | set 44 | { 45 | this["whitelist"] = value; 46 | } 47 | } 48 | 49 | public IEnumerable Whitelist 50 | { 51 | get 52 | { 53 | return WhitelistString.IsBlank() ? null : WhitelistString.Trim().Split(','); 54 | } 55 | } 56 | 57 | [ConfigurationProperty("blacklist")] 58 | public String BlacklistString 59 | { 60 | get 61 | { 62 | return (String)this["blacklist"]; 63 | } 64 | set 65 | { 66 | this["blacklist"] = value; 67 | } 68 | } 69 | 70 | public IEnumerable Blacklist 71 | { 72 | get 73 | { 74 | return BlacklistString.IsBlank() ? null : BlacklistString.Trim().Split(','); 75 | } 76 | } 77 | 78 | [ConfigurationProperty("extensionsToIgnore")] 79 | public String ExtensionsToIgnoreString 80 | { 81 | get 82 | { 83 | return (String)this["extensionsToIgnore"]; 84 | } 85 | set 86 | { 87 | this["extensionsToIgnore"] = value; 88 | } 89 | } 90 | 91 | public IEnumerable ExtensionsToIgnore 92 | { 93 | get 94 | { 95 | return ExtensionsToIgnoreString.IsBlank() ? null : ExtensionsToIgnoreString.Trim().Split(','); 96 | } 97 | } 98 | 99 | 100 | [ConfigurationProperty("crawlerUserAgents")] 101 | public String CrawlerUserAgentsString 102 | { 103 | get 104 | { 105 | return (String)this["crawlerUserAgents"]; 106 | } 107 | set 108 | { 109 | this["crawlerUserAgents"] = value; 110 | } 111 | } 112 | 113 | public IEnumerable CrawlerUserAgents 114 | { 115 | get 116 | { 117 | return CrawlerUserAgentsString.IsBlank() ? null : CrawlerUserAgentsString.Trim().Split(','); 118 | } 119 | } 120 | 121 | [ConfigurationProperty("Proxy")] 122 | public ProxyConfigElement Proxy 123 | { 124 | get 125 | { 126 | return (ProxyConfigElement)this["Proxy"]; 127 | } 128 | set 129 | { 130 | this["Proxy"] = value; 131 | } 132 | } 133 | 134 | [ConfigurationProperty("token")] 135 | public String Token 136 | { 137 | get 138 | { 139 | return (String)this["token"]; 140 | } 141 | set 142 | { 143 | this["token"] = value; 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Prerender.io/PrerenderModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Text; 9 | using System.Text.RegularExpressions; 10 | using System.Web; 11 | 12 | namespace Prerender.io 13 | { 14 | public class PrerenderModule : IHttpModule 15 | { 16 | private PrerenderConfigSection _prerenderConfig; 17 | private HttpApplication _context; 18 | private static readonly string PRERENDER_SECTION_KEY = "prerender"; 19 | private static readonly string _Escaped_Fragment = "_escaped_fragment_"; 20 | 21 | public void Dispose() 22 | { 23 | 24 | } 25 | 26 | public void Init(HttpApplication context) 27 | { 28 | this._context = context; 29 | _prerenderConfig = ConfigurationManager.GetSection(PRERENDER_SECTION_KEY) as PrerenderConfigSection; 30 | 31 | context.BeginRequest += context_BeginRequest; 32 | } 33 | 34 | protected void context_BeginRequest(object sender, EventArgs e) 35 | { 36 | try 37 | { 38 | DoPrerender(_context); 39 | } 40 | catch (Exception exception) 41 | { 42 | Debug.WriteLine(exception.ToString()); 43 | } 44 | } 45 | 46 | private void DoPrerender(HttpApplication context) 47 | { 48 | var httpContext = context.Context; 49 | var request = httpContext.Request; 50 | var response = httpContext.Response; 51 | if (ShouldShowPrerenderedPage(request)) 52 | { 53 | var result = GetPrerenderedPageResponse(request); 54 | 55 | response.StatusCode = (int)result.StatusCode; 56 | 57 | // The WebHeaderCollection is horrible, so we enumerate like this! 58 | // We are adding the received headers from the prerender service 59 | for (var i = 0; i < result.Headers.Count; ++i) 60 | { 61 | var header = result.Headers.GetKey(i); 62 | var values = result.Headers.GetValues(i); 63 | 64 | if (values == null) continue; 65 | 66 | foreach (var value in values) 67 | { 68 | response.Headers.Add(header, value); 69 | } 70 | } 71 | 72 | response.Write(result.ResponseBody); 73 | response.Flush(); 74 | context.CompleteRequest(); 75 | } 76 | } 77 | 78 | private ResponseResult GetPrerenderedPageResponse(HttpRequest request) 79 | { 80 | var apiUrl = GetApiUrl(request); 81 | var webRequest = (HttpWebRequest)WebRequest.Create(apiUrl); 82 | webRequest.Method = "GET"; 83 | webRequest.UserAgent = request.UserAgent; 84 | webRequest.AllowAutoRedirect = false; 85 | SetProxy(webRequest); 86 | SetNoCache(webRequest); 87 | 88 | // Add our key! 89 | if (_prerenderConfig.Token.IsNotBlank()) 90 | { 91 | webRequest.Headers.Add("X-Prerender-Token", _prerenderConfig.Token); 92 | } 93 | 94 | try 95 | { 96 | // Get the web response and read content etc. if successful 97 | var webResponse = (HttpWebResponse) webRequest.GetResponse(); 98 | var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8); 99 | return new ResponseResult(webResponse.StatusCode, reader.ReadToEnd(), webResponse.Headers); 100 | } 101 | catch (WebException e) 102 | { 103 | // Handle response WebExceptions for invalid renders (404s, 504s etc.) - but we still want the content 104 | var reader = new StreamReader(e.Response.GetResponseStream(), Encoding.UTF8); 105 | return new ResponseResult(((HttpWebResponse)e.Response).StatusCode, reader.ReadToEnd(), e.Response.Headers); 106 | } 107 | } 108 | 109 | private void SetProxy(HttpWebRequest webRequest) 110 | { 111 | if (_prerenderConfig.Proxy != null && _prerenderConfig.Proxy.Url.IsNotBlank()) 112 | { 113 | webRequest.Proxy = new WebProxy(_prerenderConfig.Proxy.Url, _prerenderConfig.Proxy.Port); 114 | } 115 | } 116 | 117 | private static void SetNoCache(HttpWebRequest webRequest) 118 | { 119 | webRequest.Headers.Add("Cache-Control", "no-cache"); 120 | webRequest.ContentType = "text/html"; 121 | } 122 | 123 | private String GetApiUrl(HttpRequest request) 124 | { 125 | // var url = request.Url.AbsoluteUri; (not working with angularjs) 126 | // use request.RawUrl instead of request.Url.AbsoluteUri to get the original url 127 | // becuase angularjs requires a rewrite and requests are rewritten to base / 128 | var url = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Authority, request.RawUrl); 129 | 130 | // request.RawUrl have the _escaped_fragment_ query string 131 | // Prerender server remove it before making a request, but caching plugins happen before prerender server remove it 132 | url = RemoveQueryStringByKey(url, "_escaped_fragment_"); 133 | 134 | // Correct for HTTPS if that is what the request arrived at the load balancer as 135 | // (AWS and some other load balancers hide the HTTPS from us as we terminate SSL at the load balancer!) 136 | if (string.Equals(request.Headers["X-Forwarded-Proto"], "https", StringComparison.InvariantCultureIgnoreCase)) 137 | { 138 | url = url.Replace("http://", "https://"); 139 | } 140 | 141 | // Remove the application from the URL 142 | if (_prerenderConfig.StripApplicationNameFromRequestUrl && !string.IsNullOrEmpty(request.ApplicationPath) && request.ApplicationPath != "/") 143 | { 144 | // http://test.com/MyApp/?_escape_=/somewhere 145 | url = url.Replace(request.ApplicationPath, string.Empty); 146 | } 147 | 148 | var prerenderServiceUrl = _prerenderConfig.PrerenderServiceUrl; 149 | return prerenderServiceUrl.EndsWith("/") 150 | ? (prerenderServiceUrl + url) 151 | : string.Format("{0}/{1}", prerenderServiceUrl, url); 152 | } 153 | 154 | public static string RemoveQueryStringByKey(string url, string key) 155 | { 156 | var uri = new Uri(url); 157 | 158 | // this gets all the query string key value pairs as a collection 159 | var newQueryString = HttpUtility.ParseQueryString(uri.Query); 160 | 161 | // this removes the key if exists 162 | newQueryString.Remove(key); 163 | 164 | // this gets the page path from root without QueryString 165 | string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path); 166 | 167 | return newQueryString.Count > 0 168 | ? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString) 169 | : pagePathWithoutQueryString; 170 | } 171 | 172 | 173 | private bool ShouldShowPrerenderedPage(HttpRequest request) 174 | { 175 | var userAgent = request.UserAgent; 176 | var url = request.Url; 177 | var referer = request.UrlReferrer == null ? string.Empty : request.UrlReferrer.AbsoluteUri; 178 | 179 | var blacklist = _prerenderConfig.Blacklist; 180 | if (blacklist != null && IsInBlackList(url, referer, blacklist)) 181 | 182 | 183 | 184 | 185 | { 186 | return false; 187 | } 188 | 189 | var whiteList = _prerenderConfig.Whitelist; 190 | if (whiteList != null && !IsInWhiteList(url, whiteList)) 191 | { 192 | return false; 193 | } 194 | 195 | if (HasEscapedFragment(request)) 196 | { 197 | return true; 198 | } 199 | if (userAgent.IsBlank()) 200 | { 201 | return false; 202 | } 203 | 204 | if (!IsInSearchUserAgent(userAgent)) 205 | { 206 | return false; 207 | } 208 | if (IsInResources(url)) 209 | { 210 | return false; 211 | } 212 | return true; 213 | 214 | } 215 | 216 | private bool IsInBlackList(Uri url, string referer, IEnumerable blacklist) 217 | { 218 | return blacklist.Any(item => 219 | { 220 | var regex = new Regex(item); 221 | return regex.IsMatch(url.AbsoluteUri) || (referer.IsNotBlank() && regex.IsMatch(referer)); 222 | }); 223 | } 224 | 225 | private bool IsInWhiteList(Uri url, IEnumerable whiteList) 226 | { 227 | return whiteList.Any(item => new Regex(item).IsMatch(url.AbsoluteUri)); 228 | } 229 | 230 | private bool IsInResources(Uri url) 231 | { 232 | var extensionsToIgnore = GetExtensionsToIgnore(); 233 | return extensionsToIgnore.Any(item => url.AbsoluteUri.ToLower().Contains(item.ToLower())); 234 | } 235 | 236 | private IEnumerable GetExtensionsToIgnore() 237 | { 238 | var extensionsToIgnore = new List(new[]{".js", ".css", ".less", ".png", ".jpg", ".jpeg", 239 | ".gif", ".pdf", ".doc", ".txt", ".zip", ".mp3", ".rar", ".exe", ".wmv", ".doc", ".avi", ".ppt", ".mpg", 240 | ".mpeg", ".tif", ".wav", ".mov", ".psd", ".ai", ".xls", ".mp4", ".m4a", ".swf", ".dat", ".dmg", 241 | ".iso", ".flv", ".m4v", ".torrent"}); 242 | if (_prerenderConfig.ExtensionsToIgnore.IsNotEmpty()) 243 | { 244 | extensionsToIgnore.AddRange(_prerenderConfig.ExtensionsToIgnore); 245 | } 246 | return extensionsToIgnore; 247 | } 248 | 249 | private bool IsInSearchUserAgent(string useAgent) 250 | { 251 | var crawlerUserAgents = GetCrawlerUserAgents(); 252 | 253 | // We need to see if the user agent actually contains any of the partial user agents we have! 254 | // THE ORIGINAL version compared for an exact match...! 255 | return 256 | (crawlerUserAgents.Any( 257 | crawlerUserAgent => 258 | useAgent.IndexOf(crawlerUserAgent, StringComparison.InvariantCultureIgnoreCase) >= 0)); 259 | } 260 | 261 | private IEnumerable GetCrawlerUserAgents() 262 | { 263 | var crawlerUserAgents = new List(new[] 264 | { 265 | "googlebot", "yahoo", "bingbot", "yandex", "baiduspider", "facebookexternalhit", "twitterbot", "rogerbot", "linkedinbot", 266 | "embedly", "quora link preview", "showyoubot", "outbrain", "pinterest/0.", 267 | "developers.google.com/+/web/snippet", "slackbot", "vkShare", "W3C_Validator", 268 | "redditbot", "Applebot", "WhatsApp", "flipboard", "tumblr", "bitlybot", 269 | "SkypeUriPreview", "nuzzel", "Discordbot", "Google Page Speed", "x-bufferbot" 270 | }); 271 | 272 | if (_prerenderConfig.CrawlerUserAgents.IsNotEmpty()) 273 | { 274 | crawlerUserAgents.AddRange(_prerenderConfig.CrawlerUserAgents); 275 | } 276 | return crawlerUserAgents; 277 | } 278 | 279 | private bool HasEscapedFragment(HttpRequest request) 280 | { 281 | return request.QueryString.AllKeys.Contains(_Escaped_Fragment); 282 | } 283 | 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /Prerender.io/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Prerender.io")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("Prerender.io")] 10 | [assembly: AssemblyCopyright("Copyright © 2014")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("fee09175-4250-430a-bef0-96eb1f5165ea")] 17 | 18 | [assembly: AssemblyVersion("1.0.0.2")] 19 | [assembly: AssemblyFileVersion("1.0.0.2")] 20 | -------------------------------------------------------------------------------- /Prerender.io/ProxyConfigElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Configuration; 3 | 4 | namespace Prerender.io 5 | { 6 | public class ProxyConfigElement : ConfigurationElement 7 | { 8 | [ConfigurationProperty("url")] 9 | public String Url 10 | { 11 | get 12 | { 13 | return (String)this["url"]; 14 | } 15 | set 16 | { 17 | this["url"] = value; 18 | } 19 | } 20 | 21 | [ConfigurationProperty("port", DefaultValue = 80)] 22 | public int Port 23 | { 24 | get 25 | { 26 | return (int)this["port"]; 27 | } 28 | set 29 | { 30 | this["port"] = value; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Prerender.io/ResponseResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Prerender.io 5 | { 6 | public class ResponseResult 7 | { 8 | public HttpStatusCode StatusCode 9 | { 10 | private set; 11 | get; 12 | } 13 | 14 | public String ResponseBody 15 | { 16 | private set; 17 | get; 18 | } 19 | 20 | public WebHeaderCollection Headers 21 | { 22 | private set; 23 | get; 24 | } 25 | 26 | public ResponseResult(HttpStatusCode code, String body, WebHeaderCollection headers) 27 | { 28 | StatusCode = code; 29 | ResponseBody = body; 30 | Headers = headers; 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /Prerender.io/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Prerender.io 5 | { 6 | public static class Utils 7 | { 8 | public static bool IsBlank(this string str) 9 | { 10 | return str == null || str.Trim() == string.Empty; 11 | } 12 | 13 | 14 | public static bool IsNotBlank(this string str) 15 | { 16 | return str != null && str.Trim() != string.Empty; 17 | } 18 | 19 | public static bool IsNotEmpty(this IEnumerable list) 20 | { 21 | return list != null && list.Any(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Prerender.io middleware for ASP.NET projects 2 | ============================================ 3 | 4 | Are you using backbone, angular, emberjs, etc, but you're unsure about the SEO implications? 5 | 6 | Use this filter that prerenders a javascript-rendered page using an external service and returns the HTML to the search engine crawler for SEO. 7 | 8 | `Note:` If you are using a `#` in your urls, make sure to change it to `#!`. [View Google's ajax crawling protocol](https://developers.google.com/webmasters/ajax-crawling/docs/getting-started) 9 | 10 | `Note:` Make sure you have more than one webserver thread/process running because the prerender service will make a request to your server to render the HTML. 11 | 12 | Demo project moved to [Prerender_asp_mvc_demo](https://github.com/greengerong/Prerender_asp_mvc_demo). 13 | 14 | ## Installing the middleware 15 | 16 | 1: Do a build of this project and include and reference the DLL in your web application 17 | 18 | 2: Add the http module to your web.config: 19 | 20 | 21 | 22 | 23 | 24 | 3: Sign up to get a [token](https://prerender.io/signup) and add a prerender section to the web.config file containing your token: 25 | 26 | 27 |
28 | 29 | 30 | 32 | 33 | 34 | 4: You can add following additional attributes to the prerender section to override or add to the custom settings (see PrerenderModule.cs): 35 | 36 | - prerenderServiceUrl 37 | - stripApplicationNameFromRequestUrl 38 | - whitelist 39 | - blacklist 40 | - extensionsToIgnore 41 | - crawlerUserAgents 42 | 43 | 5: Create a new class called PreApplicationStartCode in the App_Start folder: 44 | 45 | using System; 46 | using System.Collections.Generic; 47 | using System.Linq; 48 | using System.Text; 49 | using Microsoft.Web.Infrastructure.DynamicModuleHelper; 50 | using Microsoft.Web.WebPages.OAuth; 51 | using Demo.Models; 52 | 53 | namespace Demo 54 | { 55 | public static class PreApplicationStartCode 56 | { 57 | private static bool _isStarting; 58 | 59 | public static void PreStart() 60 | { 61 | if (!_isStarting) 62 | { 63 | _isStarting = true; 64 | 65 | DynamicModuleUtility.RegisterModule(typeof(Prerender.io.PrerenderModule)); 66 | } 67 | } 68 | } 69 | } 70 | 71 | 6: Add this line to the bottom of the AssemblyInfo.cs file( [PreApplicationStartCode](https://github.com/greengerong/Prerender_asp_mvc_demo/blob/master/App_Start/PreApplicationStartCode.cs)): 72 | ``` 73 | [assembly: PreApplicationStartMethod(typeof(Demo.PreApplicationStartCode), "PreStart")] 74 | ``` 75 | 76 | 7: Build and publish you web application. 77 | 78 | 8: To make shure the middleware works correctly, you can create a first request by surfing to `[YOURURL]?_escaped_fragment_=`, then log into your [Prerender.io](https://prerender.io) account. If there are errors shown in red, something went wrong. Otherwise, you're good to go! 79 | 80 | ## How it works / Testing 81 | 1. Check to make sure we should show a prerendered page 82 | 1. Check if the request is from a crawler (`_escaped_fragment_` or agent string) 83 | 2. Check to make sure we aren't requesting a resource (js, css, etc...) 84 | 3. (optional) Check to make sure the url is in the whitelist 85 | 4. (optional) Check to make sure the url isn't in the blacklist 86 | 2. Make a `GET` request to the [prerender service](https://github.com/collectiveip/prerender)(phantomjs server) for the page's prerendered HTML 87 | 3. Return that HTML to the crawler 88 | 89 | #### OR 90 | 91 | ##### Mac: 92 | 1. Open the Developer Tools in Chrome (Cmd + Atl + J) 93 | 2. Click the Settings gear in the bottom right corner. 94 | 3. Click "Overrides" on the left side of the settings panel. 95 | 4. Check the "User Agent" checkbox. 96 | 6. Choose "Other..." from the User Agent dropdown. 97 | 7. Type `googlebot` into the input box. 98 | 8. Refresh the page (make sure to keep the developer tools open). 99 | 100 | ##### Windows: 101 | 1. Open the Developer Tools in Chrome (Ctrl + shift + i) 102 | 2. Open settings (F1) 103 | 3. Click "Devices" on the left side of the settings panel. 104 | 4. Click "Add custom device..." 105 | 6. Choose a name (eg. Googlebot), screen size and enter the following User agent string: 106 | ``` 107 | Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) 108 | ``` 109 | 7. Make sure the new device is checked. 110 | 8. You can now choose it from the device dropdown in the Developer Tools screen. 111 | 112 | ## Using your own prerender service 113 | 114 | If you've deployed the prerender service on your own, set the `PRERENDER_SERVICE_URL` environment variable so that this package points there instead. Otherwise, it will default to the service already deployed at `http://service.prerender.io` 115 | 116 | $ export PRERENDER_SERVICE_URL= 117 | 118 | Or on heroku: 119 | 120 | $ heroku config:add PRERENDER_SERVICE_URL= 121 | 122 | As an alternative, you can pass `prerender_service_url` in the options object during initialization of the middleware 123 | 124 | ## License 125 | 126 | The MIT License (MIT) 127 | 128 | ## TODO: 129 | 130 | * upgrade version.(wait for my local env.) 131 | --------------------------------------------------------------------------------