Skip to main content
AI in Production 2026 is now open for talk proposals.
Share insights that help teams build, scale, and maintain stronger AI systems.
items
Menu
  • About
    • Overview 
    • Join Us  
    • Community 
    • Contact 
  • Training
    • Overview 
    • Course Catalogue 
    • Public Courses 
  • Posit
    • Overview 
    • License Resale 
    • Managed Services 
    • Health Check 
  • Data Science
    • Overview 
    • Visualisation & Dashboards 
    • Open-source Data Science 
    • Data Science as a Service 
    • Gallery 
  • Engineering
    • Overview 
    • Cloud Solutions 
    • Enterprise Applications 
  • Our Work
    • Blog 
    • Case Studies 
    • R Package Validation 
    • diffify  

Security Headers for Shiny Applications

Author: Colin Gillespie

Published: January 11, 2024

tags: r, csp, shiny, server, headers

Over the last few years, we have been performing audits on Posit set-ups, Shiny Applications and general R set-ups. One of our standard checks is to examine the server headers of a Shiny Server. Numerous websites do this check for you, but as we have an R-based/Quarto workflow, it was helpful to write a quick R package.

The package isn’t on CRAN, but is on the R-universe, so installing is straightforward

install.packages("serverHeaders",
                 repos = c("https://jumpingrivers.r-universe.dev",
                           "https://cloud.r-project.org"))

There are only a couple of exported functions. The core function is check(). As an example, let’s use jumpingrivers.com.

# check returns an invisible data frame of results
serverHeaders::check("jumpingrivers.com")
## 
## ── Checking Server ──
## 
## ✔ Status code: 301 → 301 → 200
## ✔ SSL available
## ✔ SSL redirection successful: http -> https
## ✔ content-security-policy: Policy present but not parsed
## ✔ content-type: charset set
## ✔ permissions-policy: Value present but not verified
## ✔ referrer-policy: Acceptable setting found
## ✔ strict-transport-security: max_age = 365 days and is greater than 1 year
## ✔ x-content-type-options: Acceptable setting found
## ✔ x-frame-options: Acceptable setting found

The output to the console highlights key server headers that we are interested in. Of course, the definition of key is open to a lot of discussion, but we just used securityheaders.com for guidance.

Comments on jumpingrivers.com

Before we go further, it’s worth noting that a few years ago we decided to move from Wordpress to a static site generator - Hugo. We made this decision based on

  • static sites are faster;
  • static sites are easier to maintain;
  • our previous site (WordPress) had to be constantly updated; dealing with numerous WordPress plugins always worried us - too much much for what is essentially a simple site.

One of the significant consequences of having a static site is the attack surface is significantly reduced.

Status codes

The first header is the status code. You’re probably familiar with a status code of 200 indicating a successful request, and the dreaded 404 indicating a missing page. However, when we look at jumpingrivers.com, we actually got three status codes: 301, 301, and then the magical 200. This is fairly standard. What happens is that jumpingrivers.com is actually the same as http://jumpingrivers.com. This redirects (code 301) to https://jumpingrivers.com which redirects to https://www.jumpingrivers.com

A “bad” site, wouldn’t redirect to the “https” version.

Content security policy

We’ve covered Content Security Policies (or CSP) in previous blog posts. By being explicit about where external resources are loaded from, e.g. Javascript, it gives applications an extra layer of security.

For example, we can state that Javascript can only be loaded from jumpingrivers.com and example.com. Any JavaScript resource that is loaded from another site is automatically blocked by the browser. This safeguards against attacks such as cross-site scripting.

As jumpingrivers.com is a static site (we use Hugo), we don’t need to worry about cross-site scripting quite as much; it’s probably overkill. However, adding CSP to our site has highlighted exactly where we load external resources from and has encouraged us to keep resources local where possible.

Permissions policy

Permissions policy is similar to CSPs. Essentially, we specify the resources we would load on our website. For example, would we expect to use a camera or microphone? Again, for our static site this is overkill, but for a Shiny application it’s certainly something you should consider.

Want to ensure that your application or dashboard follows the latest standards? You might benefit from our Shiny health check.

Referrer policy

When someone clicks a link on a site that takes them to another domain, the destination site receives information about where that user came from. This is how we get website analytics about our site traffic.

This isn’t too important for a site like jumpingrivers.com as we don’t have anything private on our site - everything is open to the world! However, if your URL contains potentially private information that you don’t want to be leaked, e.g. example.com/private-info then you should set the Referrer Policy.

For jumpingrivers.com, we set it to no-referrer-when-downgrade. This means when going from https to http, we won’t send the referrer header. Other than that, we’ll send the full path.

Strict transport security

This header informs browsers that a site should only be accessed using HTTPS. Once set, any future visits will automatically convert http to https. Remember, from the status code, that typing jumpingrivers.com into a browser, the URL automatically resolves to http://jumpingrivers.com, so this (after the first visit) tightens up this issue.

X content type options

This stops a browser from trying to MIME-sniff the content type. This should be set to x-content-type-options: nosniff.

X frame options

This tells the browser whether or not you want to allow your site to be framed. At jumpingrivers.com this is set to DENY.

Shiny servers

The {serverHeaders} package checks common security related headers. There are certainly others, but the headers described above are certainly the important one. Many Shiny applications we work with contain sensitive data, help make business critical decisions and/or are fundamental to a business process. As such, spending some time securing your server is to be recommended (a little bit of understatement here).

Acknowlegements

This package is based on a package originally created by Bob Rudis - hdrs.


Jumping Rivers Logo

Recent Posts

  • Start 2026 Ahead of the Curve: Boost Your Career with Jumping Rivers Training 
  • Should I Use Figma Design for Dashboard Prototyping? 
  • Announcing AI in Production 2026: A New Conference for AI and ML Practitioners 
  • Elevate Your Skills and Boost Your Career – Free Jumping Rivers Webinar on 20th November! 
  • Get Involved in the Data Science Community at our Free Meetups 
  • Polars and Pandas - Working with the Data-Frame 
  • Highlights from Shiny in Production (2025) 
  • Elevate Your Data Skills with Jumping Rivers Training 
  • Creating a Python Package with Poetry for Beginners Part2 
  • What's new for Python in 2025? 

Top Tags

  • R (236) 
  • Rbloggers (182) 
  • Pybloggers (89) 
  • Python (89) 
  • Shiny (63) 
  • Events (26) 
  • Training (23) 
  • Machine Learning (22) 
  • Conferences (20) 
  • Tidyverse (17) 
  • Statistics (14) 
  • Packages (13) 

Authors

  • Amieroh Abrahams 
  • Aida Gjoka 
  • Shane Halloran 
  • Osheen MacOscar 
  • Keith Newman 
  • Tim Brock 
  • Russ Hyde 
  • Gigi Kenneth 
  • Sebastian Mellor 
  • Myles Mitchell 
  • Theo Roe 
  • Colin Gillespie 
  • Pedro Silva 

Keep Updated

Like data science? R? Python? Stan? Then you’ll love the Jumping Rivers newsletter. The perks of being part of the Jumping Rivers family are:

  • Be the first to know about our latest courses and conferences.
  • Get discounts on the latest courses.
  • Read news on the latest techniques with the Jumping Rivers blog.

We keep your data secure and will never share your details. By subscribing, you agree to our privacy policy.

Follow Us

  • GitHub
  • Bluesky
  • LinkedIn
  • YouTube
  • Eventbrite

Find Us

The Catalyst Newcastle Helix Newcastle, NE4 5TG
Get directions

Contact Us

  • hello@jumpingrivers.com
  • + 44(0) 191 432 4340

Newsletter

Sign up

Events

  • North East Data Scientists Meetup
  • Leeds Data Science Meetup
  • Shiny in Production
British Assessment Bureau, UKAS Certified logo for ISO 9001 - Quality management British Assessment Bureau, UKAS Certified logo for ISO 27001 - Information security management Cyber Essentials Certified Plus badge
  • Privacy Notice
  • |
  • Booking Terms

©2016 - present. Jumping Rivers Ltd