cs50/server#
cs50/server
is a Docker image on Docker Hub, implemented with this Dockerfile
, with which you can (easily!) serve websites with, optionally, back ends implemented in JavaScript, PHP, Python, or Ruby. (We use it to serve CS50’s own apps on AWS Elastic Beanstalk!) Essentially, it’s a lightly customized installation of Passenger, an app server, to which we’ve added support for PHP (for some of CS50’s older web apps). It also facilitates configuration of Nginx, the web server used by Passenger in in Standalone mode, via two files, httpd.conf
and server.conf
. The image itself is based on cs50/cli
, which, in turn, is based on Ubuntu 22.04, a popular distribution of Linux.
Usage#
Assuming you already have Docker installed, base your own Dockerfile
on cs50/server
as follows, exposing TCP port 8080, the server’s default:
FROM cs50/server
EXPOSE 8080
Then ensure your app is structured as follows.
If your app’s back end is implemented in Meteor
in bundled/packaged mode, ensure you have a file called
app.js
(your app’s entry point file) in the same directory as yourDockerfile
.in non-bundled/packaged mode, ensure you have a file called
.meteor
in the same directory as yourDockerfile
.
If your app’s back end is implemented in Node.js, ensure you have a file called
app.js
(your app’s entry point file) in the same directory as yourDockerfile
.If you have a file called
package.json
in the same directory as yourDockerfile
,npm install
will be run automatically when your image is built.
If your website’s back end is implemented in PHP, ensure that you have a directory called
public
in the same directory as yourDockerfile
, inside of which are any PHP files meant to be served publicly.If your website’s back end is implemented in Python, ensure you have a (WSGI) file called
passenger_wsgi.py
, formatted as prescribed, in the same directory as yourDockerfile
.If you have a file called
requirements.txt
in the same directory as yourDockerfile
,pip install -r requirements.txt
will be run automatically when your image is built.
If your website’s back end is implemented in Ruby (or Ruby on Rails), ensure you have a file called
config.ru
, formatted as prescribed, in the same directory as yourDockerfile
.If you have a file called
Gemfile
in the same directory as yourDockerfile
,bundle install
will be run automatically when your image is built.
If your website does not have a back end, only a front end implemented in HTML (presumably with CSS and/or JavaScript), ensure that you have a directory called
public
in the same directory as yourDockerfile
, inside of which are any HTML (and CSS and/or JavaScript) files meant to be served publicly.
Configuration#
APPLICATION_ENV
#
If you set an environment called APPLICATION_ENV
to a value of dev
, as via a docker-compose.yml
file, cs50/server
(and, in turn, Passenger) will restart your application’s back end after every HTTP request (by creating a temporary file called tmp/always_restart.txt
), thereby ensuring that any changes you make to files are noticed (and not cached).
Entry Point#
By default, cs50/server
looks for a file called app.js
, config.ru
, .meteor
, or passenger_wsgi.py
per its Usage. To configure cs50/server
to use some other file as an app’s entry point, adjust your Dockerfile
as follows, where app_type
is as prescribed and startup_file
is the relative path of the file to use.
FROM cs50/server
EXPOSE ####
...
CMD passenger start --app-type app_type --startup-file startup_file
Nginx#
You can customize cs50/server
’s installation of Nginx by adding directives as follows.
To add directives to Nginix’s
http
context, put them in a file calledhttp.conf
in the same directory as yourDockerfile
. Do not surround them withhttp {
and}
.To add directives to Nginix’s
server
context, put them in a file calledserver.conf
in the same directory as yourDockerfile
. Do not surround them withserver {
and}
.
rewrite
#
To redirect, say, /surprise
(and /surprise/
) to https://youtu.be/dQw4w9WgXcQ
, create a file called server.conf
in the same directory as your Dockerfile
, the contents of which are as follows.
rewrite ^/surprise/?$ https://youtu.be/dQw4w9WgXcQ redirect;
To redirect one domain to another (e.g., www.cs50.harvard.edu
to cs50.harvard.edu
), create a file called server.conf
in the same directory as your Dockerfile
, the contents of which are as follows.
if ($http_host = www.cs50.harvard.edu) {
rewrite (.*) https://cs50.harvard.edu$1;
}
try_files
#
To route all requests (that aren’t for actual files or directories) to public/index.php
, create a file called server.conf
in the same directory as your Dockerfile
, the contents of which are as follows.
location / {
try_files $uri $uri/ /index.php?$query_string;
}
To route all requests (that aren’t for actual files or directories) to public/index.html
(as you might for a JavaScript-based single-page app), create a file called server.conf
in the same directory as your Dockerfile
, the contents of which are as follows.
location / {
try_files $uri $uri/ /index.html;
}
Port#
By default, cs50/server
uses TCP port 8080. To configure cs50/server
to use some other port, adjust your Dockerfile
as follows, where ####
is your choice of ports:
FROM cs50/server
EXPOSE ####
...
CMD passenger start --port ####
Static Files#
By default, cs50/server
assumes that your app’s static files are in public
, which is assumed to be in the same directory as your Dockerfile
. To configure cs50/server
to look in some other directory, configure your Dockerfile
as follows, where path/to/directory
is the relative path to that directory:
FROM cs50/server
...
CMD passenger start --static-files-dir path/to/directory
Notes#
Caching#
By default, HTTP responses from apps served by cs50/server
are not cached by browsers (or proxies) because the image adds
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
to those responses.
To allow responses to be cached, create a file called server.conf
in the app’s root containing the below, which will remove those headers:
more_clear_headers 'Cache-Control' 'Expires' 'Pragma';
HTTPS#
If cs50/server
detects that it’s running behind a load balancer, whereby X-Forwarded-Proto
(an HTTP header) is set, and the value of that header is http
(the implication of which is that a client’s request used HTTP instead of HTTPS), cs50/server
will redirect the request to use HTTPS.
Inline Frames#
By default, apps based on cs50/server
cannot be iframed in other sites, as the image adds
Content-Security-Policy: frame-ancestors 'self'
to HTTP responses. To allow an app to be iframed by another site, create a file called server.conf
in the app’s root containing the below, which will remove that header:
more_clear_headers 'Content-Security-Policy';