Go to file
2019-11-04 02:30:57 +01:00
cmd exposed listen addr parameter 2019-11-04 02:30:57 +01:00
contrib added documentation and conf files in contrib 2019-11-01 15:55:56 +01:00
server exposed configuration 2019-11-01 16:24:22 +01:00
go.mod added cache 2019-11-01 14:25:11 +01:00
go.sum added cache 2019-11-01 14:25:11 +01:00
LICENSE initial 2019-10-29 21:37:31 +01:00
main.go initial 2019-10-29 21:37:31 +01:00
README.md updated readme 2019-11-01 17:49:18 +01:00

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;
        }
    }
}