In this post, we explain how you can turn your Shiny Open Source server into a Shiny https server.
This tutorial is part three of a series, namely:
- Setting up an AWS instance for R.
- Installing Shiny Server on AWS
- Shiny https (this one)
- Shiny password protection
In a future post, we will explain how you can secure Shiny Server Open Source with user/password access.
This tutorial builds on Amazon AWS. But it is easy to adopt it to other cloud services or a local machine.
Https, what’s that?
Https is a protocol that encrypts your communication with a web server. For a one-minute definition, see here. Https can be useful for two things:
- So nobody can read the communication between you and the web server
- So you can be sure that you are really really talking to the desired web server, and not to a fake (a so-called man-in-the-middle)
What is an SSL certificate?
An SSL certificate binds together a domain name, a publisher of data or services, and a cryptographic key. In simple terms, it makes sure that when you connect to www.gmail.com your are really connected with google, and not with somebody who pretends to be google.
An SSL certificate is required for any https communication. Typically, you would buy an SSL certificate from a certification authority like VeriSign, Comodo, digicert, or many others. They come in various flavors and prices, and your browser typically reacts differently based on the strength of the certificate (e.g. by displaying an orange lock, a green lock, or a warning page).
For the sake of this tutorial, we will create our own SSL certificate. It will make sure that the communication to our Shiny server is encrypted. However, most browsers will display a warning when you access your Shiny app. The reason for this is that no certification authority has checked your identity. Feel free, however, to replace the the SSL certificate with a commercially obtained one.
Why would I want to have a Shiny https server?
Without a password-protected Shiny server, there are no secrets, really. So, encrypting the communication does not seem to be overly important, right?
However, making sure that your users are indeed talking to you might be important. For example, consider the case of a finance researcher that publishes on a regular basis a widely used index on company data. If people are using this data e.g. for trading, they want to be sure that the source of the data is indeed the researcher, and not an ill-natured man in the middle that wants to influence the markets to his benefit.
Shiny https (based on Shiny Server Open Source) vs. Shiny Pro?
If you are working for a company and manage to convince your boss to buy a license of Shiny Pro, by all means do that. It is a fine product and gives you advantages that go beyond securing the communication with https. The same is true for a subscription to shinyapps.io
However, if you do not have access to these financial resources, e.g. because you’re in academics or open-source development, and if you are only interested to secure your connection, then this step-by-step guide is for you.
Architecture
This guide uses the Apache HTTP web server and Amazon AWS. There are other options to achieve the same thing, but to keep the guide short we do not list them. With a bit of googling, you should be able to adapt this to other scenarios.
Our set-up will look like this:
The numbers correspond to the configuration steps we’ll follow in this guide. Specifically:
- Set up an AWS EC2 Ubuntu instance. If you haven’t done so, this tutorial tells you how.
- Set up Shiny Server Open Source: After this step, you should be able to check your setup with a regular http configuration. How to get there is explained in this tutorial.
- Set up AWS Firewall to only allow connections to our https port. This will block direct http access to the Shiny Server
- Install an SSL Certificate: Here, we’ll install a free, self-generated certificate. But if you want, you can install a bought SSL certificate that makes sure the user will not get any warnings
- Install Apache HTTP, which will manage the incoming https connections,
- Configure Apache HTTP to proxy, i.e. to translate/forward incoming https connections to http Shiny Server
If you know your way around AWS and Linux, you’ll be able to finish the entire set-up in about 15 minutes. If this is all news to you, count on spending one or two hours until everything is working properly.
Step-by-step guide
1. Create AWS EC2 Ubuntu instance
Again, see here.
2. Install Shiny Server
If you haven’t done so, check out this post.
3. Block http by configuring firewall
Log into the AWS management console and go to EC2. If you don’t know what the security group of your instance is, go to Instances and select your instance. In the bottom part, you’ll find the Security Group. Click on it. This will get you to the Security Groups. Now, do two things:
- in Inbound Rules, remove the 3838 custom rule we had open to access Shiny server over http
- add an HTTPS rule
Your security group should look similar to this:

AWS Security Group settings: Open the HTTPS port for our Shiny https server
Now, try to access your Shiny Server by typing either
1 |
http://ec2-52-59-246-209.eu-central-1.compute.amazonaws.com:3838/ |
or
1 |
http://ec2-52-59-246-209.eu-central-1.compute.amazonaws.com/ |
Replace the Public IP of your instance, of course. You should get an error page for both cases. Don’t panic. This is expected.
4. Install an SSL certificate
Here, we will create our own SSL certificate. However, for real-world cases, you should instead install a commercially bought SSL certificate. Most commercial Certification Authorities (so-called CA) provide extensive help on how to install their certificates. A free alternative with full protection is Let’s Encrypt. It’s what I’m using for my servers, and so far I haven’t had problems.
A second point of importance: In a real world scenario, you would secure a domain name that you own. Note, however, that AWS will assign a new IP address when you stop and re-start your instance. So, if you intend to go beyond just trying it out once, then make sure you at least reserve an elastic IP, so you can keep using your certificate even if you need to restart your instance. An elastic IP is an IP address reserved for you, for use on AWS. As IP addresses are scarce, AWS charges you for not using them. In theory, you could also secure an IP address instead of a DNS name. However, in the context of AWS EC2, this doesn’t make a big difference, as the public DNS name of you instance is based on its IP. If you you want to configure a domain name that you own, you will want to look into Route 53, a DNS service from AWS.
So, enough talking, let’s get with it:
First, SSH into your instance. If you don’t remember how to do that, go back to the first AWS tutorial. Make sure your commands are emitted from super user (su), which avoids typing sudo all the time:
1 |
sudo -i |
Next, we generate a cryptographic key. This is a pre-requisite for creating an SSL certificate:
1 |
openssl genrsa -out /etc/ssl/private/apache.key 2048 |
Finally, we create our SSL certificate, using the key we have just created. Type:
1 |
openssl req -new -x509 -key /etc/ssl/private/apache.key -days 365 -sha256 -out /etc/ssl/certs/apache.crt |
This will ask you a few questions. The only crucial part is the Common Name. Here you need to enter the public DNS name or the public IP of your AWS instance. Again, note, that normally you would enter a domain name that you own, e.g. ‘shiny.ipub.com’ in my case. If you are just goofing around, enter the public DNS of your instance:
5. Install Apache HTTPS Web Server
1 2 3 |
apt-get install apache2 aptitude install -y build-essential libapache2-mod-proxy-html libxml2-dev |
You should now have apache installed. To install the ssl and proxy modules in apache, run the following command:
1 |
a2enmod |
This will open a dialog that asks you which modules you would like to install. Type the following:
1 |
ssl proxy proxy_ajp proxy_http rewrite deflate headers proxy_balancer proxy_connect proxy_html |
6. Configure the Reverse Proxy
Last thing to do is to configure apache to forward https calls to http port 3838. Here, we do this globally, but if you want to use your apache for something else, too, you will want to do some reading up on apache configuration.
Type:
1 |
nano /etc/apache2/sites-enabled/000-default.conf |
This will open a simple text editor.
Your config file should look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<VirtualHost *:*> SSLEngine on SSLCertificateFile /etc/ssl/certs/apache.crt SSLCertificateKeyFile /etc/ssl/private/apache.key ProxyPreserveHost On ProxyPass / http://0.0.0.0:3838/ ProxyPassReverse / http://0.0.0.0:3838/ ServerName localhost </VirtualHost> |
This does two things:
- it turns on SSL for our apache server, pointing to the certificate previously created.
- It forwards all incoming https calls to http port 3838, the default Shiny server port
Save by hitting Ctrl+O and Enter.
Finally, you need to restart apache:
1 |
service apache2 restart |
Test your Shiny https server
Try connecting to your Shiny server by typing:
1 |
https://ec2-52-59-246-209.eu-central-1.compute.amazonaws.com/ |
Of course, you need to replace the public dns name with the one of your instance.
Remember that, with a self-generated SSL certificate, most browsers display a warning. If you dare insisting to proceed, however, you will see something like this:
And, if you click on the little lock on the left of the address bar, then you’ll see that your communication is encrypted, though the identity is not entrusted:
And here we go, you have your Shiny https server! That’s all you need to add encryption to your Shiny server.
In the final post of the series, we’ll add users and passwords to our Shiny https server.
Thanks for the nice tutorial.
It seems like you are not using Apache Tomcat but Apache HTTP Server ?!
Indeed! What an embarrassing mistake 🙁
I corrected the text and will change the diagram in a minute. Thanks a lot for the notification.
Nice post! One note about SSL certificates is you can now get a free certificate thanks to the Lets Encrypt initiative at http://www.letsencrypt.org.
Can you post link to future post you mentioned for adding users and passwords to shiny https server? Thanks
Yes, the article is finally here. See Password protect Shiny Apps
This post was great! I have several simple apps on an https connection and got a certificate from Namecheap.
I have a larger app that is running on another EC2 server with just shiny server installed, no apache, all is working.
When I try to upload the larger app to the server with https, it immediately greys out when I try to go to the site. I have isolated code that doesn’t work, but it is seemingly a random. I broke the shiny app up into multiple files and am using source(…, local=T) in many places. Would the proxy somehow not source these files correctly. The same app works on the other instance without apache, and just shiny-server.
I am having the same exact problem. Any solutions found?
Thanks for pointing that out. I imagine this happens when you start a Shiny app from RStudio, right? If so, then yes, the problem is that shiny uses websockets, and calls to that need to be proxied as well. You can add the following two lines to your apache config file:
ProxyPass /p/7000/websocket/ ws://0.0.0.0:3838/p/7000/websocket
ProxyPassReverse /p/7000/websocket/ ws://0.0.0.0:3838/p/7000/websocket
And make sure you start your app using that port, e.g.
runApp(‘inst/gui/shiny’, port = 7000)
I’ll change it in the article as well.
Here is how my apache config file looks when I want to start a shiny app from R:
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket
RewriteRule /(.*) ws://0.0.0.0:7000/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket
RewriteRule /(.*) http://0.0.0.0:7000/$1 [P,L]
SSLEngine on
SSLCertificateFile /etc/ssl/certs/apache.crt
SSLCertificateKeyFile /etc/ssl/private/apache.key
ProxyPreserveHost On
ProxyPass / http://0.0.0.0:7000/
ProxyPassReverse / http://0.0.0.0:7000/
ServerName localhost
Sorry – duh… I stopped the apache service. went to my app via http (referencing the port 3838). It’s not working still so I know its something with R or the code. I’m using xlsx package and a new version of R 3.2.4 vs. 3.2.0 (where it all works). Maybe it has something to do with that. I set up the second server on AWS to try all this out and had to reinstall all my packages – is there a way to exactly copy over an instance including the R version and packages?
Thanks again for the great post!
Thank you for the post. I have gone through the series and now can’t wait for tomorrow when I will deploy something. Just to notice, I was stuck for 5 minutes to find that a line break was missing before “ProxyPreserveHost On”, since I just copied and pasted your code without giving a glance.
Indeed, thanks a lot for letting me know. Corrected now.
Thanks a lot for the post.
I got stuck in a couple of very simple points, I want to share them here in case it may help others:
– If you follow the instructions in this post right after the previous post on the series (“Installing Shiny Server on AWS”), as I did, then you may have changed the Shiny default port from 3838 to 80 in the configuration file /etc/shiny-server/shiny-server.conf. If you did that, then you will have problems when you restart apache in the step 6 of this post, because apache will forward https calls to http port 3838. The obvious solution is to set again port 3838 in the Shiny conf file.
– When you “Test your Shiny https server” in the last step of this post, don’t stupidly forget the “s” in https in the address you type in your browser.
I was following the tutorial fine until I reached the step were the conf file is edited
nano /etc/apache2/sites-enabled/000-default.conf
updated the conf file between the tags with
SSLEngine on
SSLCertificateFile /etc/ssl/certs/apache.crt
SSLCertificateKeyFile /etc/ssl/private/apache.key
ProxyPreserveHost On
ProxyPass / http://0.0.0.0:3838/
ProxyPassReverse / http://0.0.0.0:3838/
ServerName localhost
However, I could not access my server after finishing the last few tutorial steps. Then I noticed that my conf file had , which I had to change to which got everything working
Hello, I think I have the same issue as you, what is what your conf file had that you had to change?
I figured out what my problem was. I haven’t opened the https port in AWC EC2 machine. Thank you
Relly nice post. But, I got ERR_SSL_PROTOCOL_ERROR error…
Pingback: Securing Shiny Server over SSL with Apache on Ubuntu | chrisbeeley