Configuring SSL and Security Best Practices

1. Automatic SSL with Kamal 2’s Proxy

Security is critical for PWAs, and HTTPS is a mandatory requirement for service workers, push notifications, and background sync.

Kamal 2 simplifies SSL setup by integrating a built-in proxy that:

How Kamal 2 Sets Up SSL Automatically

Unlike traditional setups where SSL required manual Nginx configuration or third-party services like Certbot, Kamal 2 does everything for you.

Once Kamal is configured and the proxy is enabled, it will:

  1. Request an SSL certificate for your domain.
  2. Configure the web proxy to serve traffic only over HTTPS.
  3. Renew the certificate before it expires.

This is particularly useful because browsers block many PWA features on non-secure origins (HTTP). With Kamal 2, there is zero manual effort required to enable HTTPS.


2. Enforcing HTTPS in Rails 8

Even though Kamal 2 manages SSL externally, it’s good practice to force HTTPS inside the Rails application. This ensures that:

In config/environments/production.rb, enable SSL enforcement:

#config/environments/production.rb
Rails.application.configure do
  config.force_ssl = true
end

This will:


3. Secure Headers and Content Security Policy (CSP) Configuration

For additional security, configure secure headers to protect against cross-site scripting (XSS) and data injection attacks.

To enhance security, use Secure Headers:

gem 'secure_headers'

Configuring Secure Headers

Create an initializer at config/initializers/secure_headers.rb:

SecureHeaders::Configuration.default do |config|
  config.hsts = "max-age=31536000; includeSubDomains"
  config.x_frame_options = "DENY"
  config.x_content_type_options = "nosniff"
  config.x_xss_protection = "1; mode=block"

  config.csp = {
    default_src: ["'self'", "https://cdn.jsdelivr.net"], # Allow resources from your domain and JSDelivr
    script_src: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], # Allow scripts from Cloudflare
    img_src: ["'self'", "data:", "https://images.yourpwa.com"], # Allow images from self and external image host
    style_src: ["'self'", "https://fonts.googleapis.com"], # Allow Google Fonts for styles
  }

end

Breakdown of the Secure Headers Configuration

With this setup, even if an attacker injects malicious scripts, they won’t be executed due to CSP restrictions.


4. Preventing Common Security Threats in Rails PWAs

a) Securing Cookies and Sessions

Modify your config/initializers/session_store.rb:

Rails.application.config.session_store :cookie_store,
  key: "_my_pwa_session",
  secure: Rails.env.production?,
  httponly: true,
  same_site: :strict

b) Protecting Against CSRF (Cross-Site Request Forgery)

Rails already provides CSRF protection by default, but ensure it's enforced:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end

c) Restricting API Access with CORS

If your Rails backend serves APIs for your PWA, restrict cross-origin access by adding:

#config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "https://yourpwa.com"
    resource "*", headers: :any, methods: [:get, :post, :put, :delete]
  end
end

5. Running a Security Audit

Before deploying your Rails PWA to production, scan for security vulnerabilities:

bundle exec brakeman

Brakeman will check for XSS, SQL Injection, and other Rails security risks.


Now your Rails 8 PWA is fully deployed on Hetzner using Kamal 2 with automatic SSL, Docker-based deployment, and security best practices.