rendertronãç¨ãã¦SSRã«å¯¾å¿ãã¦ãªããµã¤ãã§ãSEOãOGP対çãè¡ã
Dynamic Rendering
ãã®ææ³ã¯Dynamic Renderingã¨å¼ã°ããSSRã«å¯¾å¿ãã¦ãªããµã¤ãã«å¯¾ãã¦ã®SEO対çã¨ãã¦æå¹ã§ããDynamic Renderingã¨ã¯ä¸è¨ã§ããã¨ããµã¼ãã¼ã§Nodeåä½ã§ã¯ãªãããã©ã¦ã¶ãåããã¤ã¡ã¼ã¸ã§ãã ããã¯SSRã¿ãããªNode.jsã®ã³ã¼ããæ¸ããã¨ãªããããå°å ¥ã³ã¹ãã¯ä½ãã§ãã
詳ããã¯ã以ä¸ã®googleã®è¨äºãèªãã§ãã ããã
ãã®è¨äºã§ã説æããã¦ããrendertronãä»åã¯ç¨ãã¾ãã
Rendertron
puppeteerãã©ããããapi serverã¿ãããªãã®ã§å
é¨ã¯koaã使ããã¦ãã¾ãããããèµ·åãã/render
ã¸urlãpathã¨ãã¦æ¿å
¥ããã¨ãã®ãã¼ã¸ã®htmlãè¿ããã¾ãã ä¾ãã°ã/render/https://google.com
ã¨ã¢ã¯ã»ã¹ããã¨ãgoogle.comã®htmlãè¿ã£ã¦ãã¾ãã ã¾ããã¹ã¯ãªã¼ã³ã·ã§ãããåããããã¾ãã(/screenshot
)
è¿ãhtmlã¯é
ä¿¡å
ã¨ã¯ä¸è´ã¯ããæé©åããããã®ãè¿ããã¾ããä¾ãã°ãconsole.log('hello')
ãdocument.write('test')
ã ãæ¸ãããjsãªã©ã¯ãhtmlã«æ¿å
¥ãããå¾ãã®ã¹ã¯ãªããã¿ã°ã¯htmlå
ãããªããªã£ãããbase
ãã¤ããããã¾ãã
ã¡ãªã¿ã«rendertronãGCPã§åããã®ã¯ãã£ã¨ç°¡åã ã£ãããã¾ãã
ã¤ã³ãã©æ§æ
ä¸è¨ã®ãªãã¸ããªã§ã¯docker-composeã§ç°¡åãªæ§æãä½ãã¾ããã
https://foo.com
ã¸ã¢ã¯ã»ã¹ãæ¥ãã¨ããNginxã§botãã©ãããå¤æãã- botã®å ´åã¯ãrendertron(internal)ã®ãµã¼ãã¼ã¸
- ã¢ã¯ã»ã¹ã®urlãrendertronã®urlã®pathã«ã¤ãã
- e.g.
http://rendertron/render/https://foo.com
- e.g.
- rendetronãindex.htmlã¸ã¢ã¯ã»ã¹ããhtmlãã¬ã³ããªã³ã°ãè¿ã
- ã¢ã¯ã»ã¹ã®urlãrendertronã®urlã®pathã«ã¤ãã
- ã¦ã¼ã¶ã¼ã®å ´åã¯ãindex.htmlãåãã«è¡ã
- botã®å ´åã¯ãrendertron(internal)ã®ãµã¼ãã¼ã¸
å段
以ä¸ãåèã«ãã¾ããã
upstream rendertron { server rendertron:3000; } map $http_user_agent $is_bot { # default 1; # if you want to debug as a bot, you should comment out this '~*googlebot' 1; } server { listen 80; server_name localhost; if ($is_bot = 1) { rewrite ^(.*)$ /rendertron/$1; } location /rendertron/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass http://rendertron/render/$scheme://storage$request_uri; } location / { proxy_pass http://storage/; } }
botã®å ´åãurlã«/rendertron
ãä»ããlocation /rendertron/
ã®åå²ã¸ããã¾ããããã¦ãproxy_pass http://rendertron/render/$scheme://storage$request_uri;
ã®ãªãã¼ã¹ãããã·ãè¨å®ãã¾ãã ãã®ããã«æ¸ããã¨ã«ãããhttp://localhost:8080
ãhttp://rendertron/render/http://localhost:8080
ã¨é£ã°ãããã«ããhtmlãè¿ãããã«ãã¾ãã
Rendertron
ç¹ã«ä½ãããªãã¦ããã§ãããpuppeteerãå°å ¥ããããã«èªåã§Dockerfileãæ¸ãã®ã¯å°ã大å¤ãªã®ã§ãä»åã¯ããã¡ãã®ã¤ã¡ã¼ã¸ã使ãã¾ããã
SPA
index.htmlãæã£ã¦ãããµã¼ãã¼ã§try_files
ãã¦ããããã¨ã«ããã404ãåé¿ããã¾ãã
# nginx.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/sample; # for spa try_files $uri $uri/ /index.html; } }
HTML, JS
ããã¯ä¾ãªã®ã§ä½ã§ããããåãµã¼ãã¹ã®ã¢ããªã±ã¼ã·ã§ã³ã³ã¼ãã¨ãªãã¾ãã
ä»åã¯ãéãã¢ããªã±ã¼ã·ã§ã³ãåããããã£ãã®ã§ããããd3ã®ãµã³ãã«ãåãã¾ããã ãããhtmlã¸ã¬ã³ããªã³ã°ããã¦ããã°æåã¨ãªãã¾ãã
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> </head> <body> <div id="observablehq-6b3f2a05"></div> <script type="module"> (async () => { const id = '#observablehq-6b3f2a05'; if (document.querySelector(id).children.length === 0) { const { Runtime, Inspector } = await import('https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js'); const { default: define } = await import('https://api.observablehq.com/@d3/hierarchical-edge-bundling.js?v=3'); const inspect = Inspector.into(id); (new Runtime).module(define, name => name === 'chart' ? inspect() : undefined); } })(); </script> <script src="main.js"></script> </body> </html>
main.jsã§ã¯ãogpãè¨å®ãããã¨æãã¾ãã
// main.js const a = document.createElement('a'); a.setAttribute('href', 'https://observablehq.com/@d3/hierarchical-edge-bundling'); a.text = 'This is hierarchical-edge-bundling, the code is here.'; document.body.append(a); // ogp const props = [ { type: 'og:url', content: 'http://localhost:8080' }, { type: 'og:type', content: 'website' }, ... ]; const fragment = document.createDocumentFragment(); props.forEach(({ type, content }) => { const meta = document.createElement('meta'); meta.setAttribute('property', type); meta.setAttribute('content', content); fragment.appendChild(meta); }); document.querySelector('head').appendChild(fragment);
çµæ
ã¦ã¼ã¶ã¼ãã¢ã¯ã»ã¹ããå ´å
ã¾ãã¾ä¸è¨ã®htmlãåºåãããã ãã¨ãªããCSRã§ãã
botãã¢ã¯ã»ã¹ããå ´å
metaã«og
ã bodyã®ä¸ã«d3ã®çµæåºåã³ã¼ããåºã¦ãã¦SSRãæåãã¾ããã
ã¾ããç»é¢ã§ã¿ã¦ãã¦ã¼ã¶ã¼ã®ã¢ã¯ã»ã¹ã¨åæ§ã®ç»é¢ã¨ãªãã¾ãã
ããã§googleããããTwitterãªã©ã®ogpã«ã対å¿ãããã¨ãå¯è½ã§ãã
åé¡ç¹
ä½æçã«ãSSRããã¯é ãæãã¾ããSSRã¯æé©åããããã®ãããã¨æãã¾ããã
SSRããã¯æ¥½ãªåãå¹çãæªãããã«ã¿ãã¾ãããä»å¾ssr-serverã¨rendertronã§åãé¯ã¹ããã¯ã§ã©ããããæããã®ããå«ãå®é¨ãã¦ã¿ãããªã¼ã£ã¦æã£ãããã¾ãã
ãããã«ããããããã®ã¯ã大è¦æ¨¡ãµã¼ãã¹ã§å®é¨ããªãã¨ããããªããã¨ãå¤ãã®ã§ä»å¾ã«æå¾ ã§ãã
ãããã«
æ¨æ¥ã®å¤ãçªç¶ãããããªã£ã¦è¨äºã«ãã¾ããã
å°å ¥ã³ã¹ãã¯ä½ãã®ã§ãä»SPAãªãµã¤ãã ãã©SSRãã¦ãªãããSEOãä¸å®ã¨ãogpãæå¹åãããï¼ã£ã¦äººã¯æ¤è¨ãã¦ã¿ã¦ãããããããªãã§ããããã