diff --git a/Procfile b/Procfile index a1550bdc0..676cdef95 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ release: bundle exec rake db:migrate -web: bundle exec puma -C config/puma.rb +web: bin/start-nginx bundle exec puma -C config/puma.rb diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index dfc926896..1191fa0a8 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -31,10 +31,10 @@ = csp_meta_tag - %script{ async: true, src: 'https://plausible.io/js/pa-PFruVsE_br97UUCRXE_6f.js' } + %script{ async: true, src: '/js/script.js' } %script window.plausible = window.plausible || function() { (plausible.q = plausible.q || []).push(arguments) }, plausible.init = plausible.init || function(i) { plausible.o = i || {} }; - plausible.init() + plausible.init({ endpoint: '/api/event' }) %body.no-js{ 'class': "#{params[:controller]}-#{params[:action]}", 'data-bs-no-jquery': 'true' } #top diff --git a/config/nginx.conf.erb b/config/nginx.conf.erb new file mode 100644 index 000000000..6a96207ce --- /dev/null +++ b/config/nginx.conf.erb @@ -0,0 +1,77 @@ +daemon off; +worker_processes auto; + +events { + worker_connections 1024; +} + +http { + charset utf-8; + server_tokens off; + + # DNS resolver for proxy_pass + resolver 9.9.9.9 valid=30s; + + # Proxy cache in /dev/shm (tmpfs, persists across dyno restarts) + proxy_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=plausible_cache:1m max_size=100m inactive=5m use_temp_path=off; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Logs to stdout/stderr for Heroku + access_log /dev/stdout; + error_log /dev/stderr; + + # Proxy settings + proxy_http_version 1.1; + proxy_buffering on; + + upstream app_server { + server unix:/tmp/nginx.socket fail_timeout=0; + } + + server { + listen <%= ENV["PORT"] %>; + server_name _; + keepalive_timeout 5; + + # Plausible endpoints (set inside server context) + set $plausible_script_url https://plausible.io/js/pa-PFruVsE_br97UUCRXE_6f.js; + set $plausible_event_url https://plausible.io/api/event; + + # Plausible: Proxy script.js (cached) + location = /js/script.js { + proxy_cache plausible_cache; + proxy_cache_valid 200 5m; + proxy_cache_key "$host$uri"; + proxy_pass $plausible_script_url; + proxy_set_header Host plausible.io; + proxy_buffering on; + + # Cache response headers + add_header X-Cache $upstream_cache_status; + } + + # Plausible: Proxy event API + location = /api/event { + proxy_pass $plausible_event_url; + proxy_set_header Host plausible.io; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header X-Forwarded-Host $host; + proxy_buffering on; + } + + # Rails: All other requests + location / { + proxy_pass http://app_server; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header Host $http_host; + proxy_redirect off; + } + } +} \ No newline at end of file diff --git a/config/puma.rb b/config/puma.rb index 0c350dd16..2ff84a33e 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,7 +1,9 @@ # This configuration file will be evaluated by Puma. The top-level methods that # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# + +require 'fileutils' + # Puma starts a configurable number of processes (workers) and each process # serves each request in a thread from an internal thread pool. # @@ -29,7 +31,16 @@ threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. -port ENV.fetch("PORT", 3000) +# Use Unix socket when nginx config exists (from heroku-community/nginx buildpack) +# Falls back to port if nginx config not present +if File.exist?("config/nginx.conf.erb") + bind "unix:///tmp/nginx.socket?umask=0077" # Restrict socket permissions to owner only + + # Signal to nginx buildpack that app is ready (required for nginx to start) + FileUtils.touch("/tmp/app-initialized") +else + port ENV.fetch("PORT", 3000) +end # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart