Skip to content
google-chrome --dump-dom go wrapper
Go
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
cmd
contrib
server
LICENSE
README.md
go.mod
go.sum
main.go

README.md

chromedom

a simple proxy for rendering SPA

how to use

configure nginx

see also https://gist.github.com/thoop/8165802

	location / {
        try_files $uri @prerender;
    }

	location @prerender {
        proxy_set_header Host $host;
        set $prerender 0;
        if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest\/0\.|pinterestbot|slackbot|vkShare|W3C_Validator|whatsapp|$
            set $prerender 1;
        }
        if ($args ~ "_escaped_fragment_") {
            set $prerender 1;
        }
        if ($http_user_agent ~ "Prerender") {
            set $prerender 0;
        }
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $prerender 0;
        }
        if ($prerender = 1) {
            proxy_pass http://127.0.0.1:9292;
        }
        if ($prerender = 0) {
            rewrite .* /index.html break;
        }
	}

configure

create dumpdom.conf

In /etc/nginx/dumpdom.conf, paste this:

map $http_user_agent $dumpdom_ua {
    default 0;

    "~*googlebot" 1;
    "~*bingbot" 1;
    "~*yandex" 1;
    "~*baiduspider" 1;
    "~*twitterbot" 1;
    "~*baiduspider" 1;
    "~*facebookexternalhit" 1;
    "~*rogerbot" 1;
    "~*linkedinbot" 1;
    "~*embedly" 1;
    "~*quora link preview" 1;
    "~*showyoubot" 1;
    "~*outbrain" 1;
    "~*pinterest" 1;
    "~*slackbot" 1;
    "~*vkShare" 1;
    "~*Slack-ImgProxy" 1;
    "~*Slackbot-LinkExpanding" 1;
    "~*Site Analyzer" 1;
    "~*SiteAnalyzerBot" 1;
    "~*Viber" 1;
    "~*Whatsapp" 1;
    "~*Telegram" 1;
    "~*W3C_Validator" 1;
    "~*DuckDuckBot" 1;
    "~*redditbot" 1;
    "~*Discordbot" 1;
    "~*Viber" 1;
}

map $args $dumpdom {
    default $dumpdom_ua;
    "~(^|&)_escaped_fragment_=" 1;
}

In your domain configuration file e.g. /etc/nginx/conf.d/domain.tld.conf

server {
        listen 80;
        listen [::]:80;
        server_name domain.tld;
        access_log off;
        root /srv/http/domain.tld/dist/chat;
        include /etc/nginx/acme.conf;
        location / {
                return 301 https://domain.tld$request_uri;
        }
}

# include the previously written dumpdom.conf file
# map is in context of http, so it should be used where
# all your nginx is serving are SPA. 
include dumpdom.conf;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name domain.tld;
    
    ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;

    access_log /var/log/nginx/domain.tld.access.log;
    error_log /var/log/nginx/domain.tld.error.log;
    # where you put the files generated by "ng build --prod"
    root /srv/http/domain.tld/dist/appname;

    proxy_intercept_errors on;
    error_page 500 502 503 504 =200 /index.html;
    # 404 is managed by the app
    error_page 404 =200 /index.html;

    location / {
        try_files $uri @dumpdom;
    }

    location @dumpdom {
        proxy_set_header Host $host;
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $dumpdom 0;
        }
        if ($dumpdom = 1) {
            proxy_pass http://127.0.0.1:9292;
        }
        if ($dumpdom = 0) {
            rewrite .* /index.html break;
        }
    }

for completeness sake here's also cors.conf

     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }

and acme.conf

location /.well-known {
        alias /srv/http/acme/.well-known;

        location ~ /.well-known/(.*) {
                default_type text/plain;
        }
}

alternative configuration

This configuration should be used if you have more than all SPA.

server {
    listen 80;
    listen [::]:80;
    server_name domain.tld;
    access_log off;
    root /srv/http/domain.tld/dist/chat;
    include /etc/nginx/acme.conf;
    location / {
        return 301 https://domain.tld$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name domain.tld;

    ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;

    access_log /var/log/nginx/domain.tld.access.log;
    error_log /var/log/nginx/domain.tld.error.log;
    root /srv/http/domain.tld/dist/appname;

    location / {
        try_files $uri @dumpdom;
    }

    location @dumpdom {
        proxy_set_header Host $host;
        set $dumpdom 0;
        if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|whatsapp|DuckDuckBot|redditbot|Discordbot|Viber") {
            set $dumpdom 1;
        }
        if ($args ~ "_escaped_fragment_") {
            set $dumpdom 1;
        }
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $dumpdom 0;
        }
        if ($dumpdom = 1) {
            proxy_pass http://127.0.0.1:9292;
        }
        if ($dumpdom = 0) {
            rewrite .* /index.html break;
        }
    }
}
You can’t perform that action at this time.