- 7.1 Showing Users
- 7.2 Signup Form
- 7.3 Unsuccessful Signups
- 7.4 Successful Signups
- 7.5 Professional-Grade Deployment
- 7.6 Conclusion
7.5 Professional-Grade Deployment
Now that we have a working signup page, it’s time to deploy our application and get it working in production. Although we started deploying our application in Chapter 3, this is the first time it will actually do something, so we’ll take this opportunity to make the deployment professional-grade. In particular, we’ll add an important feature to the production application to make signup secure, we’ll replace the default web server with one suitable for real-world use, and we’ll add some configuration for our production database.
As preparation for the deployment, you should merge your changes into the main branch at this point:
$ git add -A $ git commit -m "Finish user signup" $ git checkout main $ git merge sign-up
7.5.1 SSL in Production
When submitting the signup form developed in this chapter, the name, email address, and password get sent over the network, and hence are vulnerable to being intercepted by malicious users. This is a potentially serious security flaw in our application, and the way to fix it is to use Secure Sockets Layer (SSL)14 to encrypt all relevant information before it leaves the local browser. Although we could use SSL on just the signup page, it’s actually easier to implement it site-wide, which has the additional benefits of securing user login (Chapter 8) and making our application immune to the critical session hijacking vulnerability discussed in Section 9.1.
Although Heroku now uses SSL for all connections, it’s more secure not to rely on the web host for such behavior. Indeed, Heroku didn’t used to forward “http” to “https”, leading to insecure behavior by default (Figure 7.27).
Figure 7.27: Heroku’s former behavior using an insecure http URL in production.
We can control our app’s security ourselves by forcing all browsers to use SSL, which is as easy as uncommenting a single line in production.rb, the configuration file for production applications. As shown in Listing 7.34, all we need to do is set config.force_ssl to true.
Listing 7.34: Configuring the application to use SSL in production.
config/environments/production.rb
Rails.application.configure do . . . # Force all access to the app over SSL, use Strict-Transport-Security, # and use secure cookies. config.force_ssl = true . . . end
At this stage, we need to set up SSL on the remote server. Setting up a production site to use SSL involves purchasing and configuring an SSL certificate for your domain. That’s a lot of work, though, and luckily we won’t need it here: For an application running on a Heroku domain (such as the sample application), we can piggyback on Heroku’s SSL certificate. As a result, when we deploy the application in Section 7.5.2, SSL will automatically be enabled.
Note: If you’re using Cloudflare to handle SSL on a custom domain (as described in Learn Enough Custom Domains to Be Dangerous (https://www.learnenough.com/custom-domains)), you shouldn’t force SSL in the Rails app. Instead, you should keep the config.force_ssl line in production.rb commented out (which is the default), or else set it to false as in Listing 7.35.
Listing 7.35: Configuring a custom domain not to use SSL in production.
config/environments/production.rb
Rails.application.configure do . . . # Force all access to the app over SSL, use Strict-Transport-Security, # and use secure cookies. config.force_ssl = false . . . end
7.5.2 Production Web Server
Having added SSL, we now need to configure our application to use a web server suitable for production applications. Following Heroku’s recommendation, we’ll use Puma, an HTTP server that is capable of handling a large number of incoming requests.
To add the new web server, we simply follow the Heroku Puma documentation (https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server). The first step is to include the puma gem in our Gemfile, but as of Rails 5 Puma is included by default (Listing 3.2). This means we can skip right to the second step, which is to replace the default contents of the file config/puma.rb with the configuration shown in Listing 7.36. The code in Listing 7.36 comes straight from the Heroku documentation,15 and there is no need to understand it (Box 1.2).
Listing 7.36: The configuration file for the production web server.
config/puma.rb
# Puma configuration file. max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } threads min_threads_count, max_threads_count port ENV.fetch("PORT") { 3000 } environment ENV.fetch("RAILS_ENV") { ENV['RACK_ENV'] || "development" } pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } workers ENV.fetch("WEB_CONCURRENCY") { 2 } preload_app! plugin :tmp_restart
We also need to make a so-called Procfile to tell Heroku to run a Puma process in production, as shown in Listing 7.37. The Procfile should be created in your application’s root directory (i.e., in the same directory as the Gemfile).
Listing 7.37: Defining a Procfile for Puma.
./Procfile
web: bundle exec puma -C config/puma.rb
7.5.3 Production Database Configuration
The final step in our production deployment is properly configuring the production database, which (as mentioned briefly in Section 2.3.5) is PostgreSQL. My testing indicates that PostgreSQL actually works on Heroku without any configuration, but the official Heroku documentation recommends explicit configuration nonetheless, so we’ll err on the side of caution and include it.
The actual change is easy: All we have to do is update the production section of the database configuration file, config/database.yml. The result, which I adapted from the Heroku docs, is shown in Listing 7.38.
Listing 7.38: Configuring the database for production.
config/database.yml
# SQLite version 3.x # gem install sqlite3 # # Ensure the SQLite 3 gem is defined in your Gemfile # gem 'sqlite3' # default: &default adapter: sqlite3 pool: 5 timeout: 5000 development: <<: *default database: db/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default database: db/test.sqlite3 production: adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") 5 %> database: sample_app_production username: sample_app password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %>
7.5.4 Production Deployment
With the production web server and database configuration completed, we’re ready to commit and deploy:16
$ rails test $ git add -A $ git commit -m "Use SSL and the Puma web server in production" $ git push && git push heroku $ heroku run rails db:migrate
We can also reset the production database (never do this with a real app’s database!) as we did with the development database in Section 7.4.3:
$ heroku pg:reset DATABASE $ heroku run rails db:migrate
The signup form is now live, and the result of a successful signup is shown in Figure 7.28. Note the presence of a lock icon in the address bar of Figure 7.28, which indicates that SSL is working.
Figure 7.28: Signing up on the live Web.
Exercises
Confirm on your browser that the SSL lock and https appear.
Create a user on the production site using your primary email address. Does your Gravatar appear correctly?