I have Rails set up on my Mac and the app I’m working on seems to run as expected in development mode when started up with bin/rails server.
To get ready to deploy to production, I wanted to run the app in production mode on my Mac first. So I started the app up with rails s -e production and it started up at (http://0.0.0.0:3000).
But when I pointed my browser to (http://0.0.0.0:3000) I got back:
Access to 0.0.0.0 was denied
You don't have authorization to view this page.
HTTP ERROR 403
Where do you see this error? Is it in your console where you started the server, or in your browser?
What is your Web server setup like on your local development machine? Do you have a separate Web server, like Apache or NGINX, running at port 80 or another port?
Is this application new enough that it is actually spinning up a Docker container for the server to live in?
When you start up a (modern or semi-modern) Rails app in development mode, you are booting Puma from within the app directory, on localhost:3000. When you do the same in production, this can vary quite a lot, depending on how you have configured your application to run once deployed. Have a look at any Procfile.* files in your application root (or the config.ru) to see more detail about how the app is configured to boot.
Most of my production applications run under Passenger in Apache, with newer ones substituting NGINX for Apache, but continuing to use Passenger as the application server. Puma is at the same layer as Passenger, if you squint at it. It runs as the “application server” layer, and must have another layer above that to handle the nuance of dealing with slow/stuck browsers, static files, etc. In my case, that will be either Apache or NGINX.
A completely vanilla Rails 8.0.x application will default to using Thruster as this “protective” layer, running inside a Docker container that is, itself, deployable as your production environment using the Kamal deployment system. You would need to have enabled that container to be networked to from localhost in order to pierce its shell locally. I think. I can’t help you with that kind of setup, if that’s where this nets out. Perhaps another dev more experienced with containers will have some suggestions.
I see the error in the browser after starting the app with rails s -e production. As the server fires up I get this in my terminal:
bob@RockBox www.br.ru % rails s -e production
=> Booting Puma
=> Rails 8.0.1 application starting in production
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 6.5.0 ("Sky's Version")
* Ruby version: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-darwin24]
* Min threads: 3
* Max threads: 3
* Environment: production
* PID: 64067
* Listening on http://0.0.0.0:3000
Use Ctrl-C to stop
I don’t think I’m running Docker, Passenger, Apache, or Nginx. There are no Procfile.* in my app root. My config.ru is pretty simple:
require_relative "config/environment"
run Rails.application
Rails.application.load_server
My config/environments/production.rb (which I assume is being use) looks like:
require "active_support/core_ext/integer/time"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.enable_reloading = false
# Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
config.eager_load = true
# Full error reports are disabled.
config.consider_all_requests_local = false
# Turn on fragment caching in view templates.
config.action_controller.perform_caching = true
# Cache assets for far-future expiry since they are all digest stamped.
config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
# Skip http-to-https redirect for the default health check endpoint.
# config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
# Log to STDOUT with the current request id as a default log tag.
config.log_tags = [ :request_id ]
config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
# Change to "debug" to log everything (including potentially personally-identifiable information!)
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
# Prevent health checks from clogging up the logs.
config.silence_healthcheck_path = "/up"
# Don't log any deprecations.
config.active_support.report_deprecations = false
# Replace the default in-process memory cache store with a durable alternative.
config.cache_store = :solid_cache_store
# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "example.com" }
# Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
# config.action_mailer.smtp_settings = {
# user_name: Rails.application.credentials.dig(:smtp, :user_name),
# password: Rails.application.credentials.dig(:smtp, :password),
# address: "smtp.example.com",
# port: 587,
# authentication: :plain
# }
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# Only use :id for inspections in production.
config.active_record.attributes_for_inspect = [ :id ]
# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
#
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
end
You have force_ssl = true on your production.rb file, but are trying to access using http instead of https. Try changing it to false. Also change assume_ssl to false
Weird. That messages makes it look like you set config.hosts in production.rb, but I can see it commented.
Only thing I can see is that you are starting puma with www.br.ru %, which makes me think you have a custom startup script that might be changing something.
Try just running rails s -e production and see if it works.
fwiw - I have the exact same production.rb on a mac and it just works with your rails s -e production - here it listens on http://0.0.0.0:39693 and loads there showing a broken ssl lock - also reachable on localhost:39693 - putting config.hosts << "localhost" into production.rb does nothing - same with or without.
And now… config.hosts << "localhost" doesn’t work for me locally in production mode. I don’t think I’ve done anything to the configuration to have changed behavior, but it just quit working!
my local environment is apache2 serving from ~/Sites/ - I switched to macports recently and the /etc/apache2/httpd.conf has me as User - localhost as ServerName - Directory > AllowOverride All / Require all denied and DocumentRoot ~/Sites/ and then another Directory > ~/Sites/ - AllowOverride All > Require all granted </Directory
And then in /etc/apache2/extra/httpd-userdir.conf we have : UserDir Sites and the bonjour module unchanged I think and that is it.
and Sites has .htaccess with:
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
My local development and production run on the built-in Rails Puma server, which I thought would make things easier. I have also used the MAMP stack for working with PHP and liked it.
Maybe I’ll have to try Apache locally.
But I’m not sure how my shared hosting service runs Rails. I can’t seem to get this working locally or hosted.
I have a vps on dreamhost so kind of shared. I have to create a proxy server for rails and install a puma.service script in .config/systemd/user that runs the puma server and there is a wrapper script that runs that and restarts if need be.
The fix for me was some helpful comments on Stack Overflow that got me to understand the config.hosts setting is for the HOST and not the VISITOR. So it needs to be set to allow whatever IP is running the site.
I also learned that localhost != to 127.0.0.1, which surprised me. But allowing 127.0.0.1 for my local development computer got running Rails in production mode working there.
Also, I now understand that Propshaft runs in development but not in production mode. So the best practice is to precompile assets with RAILS_ENV=production rails assets:precompile before deployment (either locally or remotely).