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:
- Automatically provisions SSL certificates via Let's Encrypt.
- Handles SSL renewal without manual intervention.
- Enforces HTTPS by default, redirecting HTTP traffic securely.
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:
- Request an SSL certificate for your domain.
- Configure the web proxy to serve traffic only over HTTPS.
- 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:
- All cookies and session data are transmitted securely.
- Any accidental HTTP requests are redirected to HTTPS.
- Mixed content errors (HTTP assets in an HTTPS page) are prevented.
In config/environments/production.rb, enable SSL enforcement:
#config/environments/production.rb
Rails.application.configure do
config.force_ssl = true
end
This will:
- ✅ Redirect all HTTP requests to HTTPS.
- ✅ Secure cookies with the Secure and HttpOnly flags.
- ✅ Prevent browsers from loading mixed HTTP content.
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
- HSTS (HTTP Strict Transport Security): Forces HTTPS at the browser level.
- X-Frame-Options: Prevents clickjacking attacks by disallowing iframe embedding.
- X-Content-Type-Options: Stops browsers from guessing MIME types (prevents MIME-sniffing attacks).
- X-XSS-Protection: Enables built-in XSS filtering in browsers.
- Content Security Policy (CSP): Limits the sources from which scripts and resources can be loaded.
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
- secure: Rails.env.production? → Ensures cookies are only sent over HTTPS.
- httponly: true → Prevents JavaScript from accessing cookies.
- same_site: :strict → Blocks third-party cookies to mitigate CSRF attacks.
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.