Encrypt and host a knitted R Markdown file


May 7, 2021

Several plastic stitch markers, for keeping track of knit counts, on a mousemat that has a tidyverse hex design by Amelia McNamara.

An attempted knitting/padlock visual pun with stitch markers.


You can knit an R Markdown file to an encrypted HTML with {encryptedRmd} and put it online with GitHub Pages. Users must enter the decryption key to download and view the content.

TinkR TailR

I’m working on a personal project that outputs an HTML document rendered from an R Markdown file. I want to password-protect and share it with a specific person somehow. Also, sharing a hard copy by email is tedious; I’d rather they always had the latest version from a URL.

More importantly, I watched Tinker Tailor Soldier Spy1 over the weekend and I wanted to feign top-secret document handling.

Invest in encrypto

I’m a simple fellow. I don’t want to download or handle any extra software; I don’t want to configure anything; I like ‘free’ as in ‘I don’t have to pay for it’; I want minimal friction for the person accessing the content; I want the file to be standalone and self-contained.

There’s probably many ways to achieve this with R, but I decided to try out Dirk Schumacher’s {encryptedRmd} package, which I starred on GitHub a while ago and then forgot about.2 I’ve used GitHub Pages for hosting a bunch of stuff at no cost; might as well pop it there.


I made a simple GitHub repo containing a demo of this approach. The encrypted HTML output is available online. I’ve embedded it below as well.

You’ll be asked for a key to decrypt it. For this demo, the key is: 9ebf5d40b061c59330b9fce16703e4c2fb2fbf90a773996e43e49d562ea17039.

Your browser will prompt you to save and view the decrypted document if you enter the correct key. Prepare to see some extremely top secret information when you open that file.

How to

How was this created? In short, you knit an R Markdown as usual, but replace the output type in the YAML header. Here’s the steps:

  1. Install {encryptedRmd} from CRAN if you haven’t already, using install.packages("encryptedRmd")
  2. Write an R Markdown file (i.e. extension .Rmd) where the output: field in the YAML header is set to encryptedRmd::encrypted_html_document
  3. Knit the file to render (i) an unencrypted ‘normal’ version HTML, (ii) an encrypted HTML version, and (iii) a text file containing a randomised decryption key, all of which are generated by default in the same folder that the Rmd is in
  4. To serve, commit and push only the encrypted file to GitHub and then activate GitHub Pages for the repo (go to ‘Settings’ > ‘Pages’)

Of course, you should add the R Markdown file, the unencrypted HTML and the decryption key to your .gitignore file, otherwise the content will be available for people to see.

I’ve retained all the output files in the demo repo so you can see them all. You can find them in the docs/ folder:

├── encrypt-test.Rmd
├── encrypt-test.html
├── encrypt-test.enc.html
└── encrypt-test.enc.html.key

I told GitHub Pages to serve this docs/ folder from the main branch, so that the URL ends up as https://matt-dray.github.io/encrypt-rmd-test/encrypt-test.html.enc.html.3

Now I can share the URL with someone and send them the key via a separate means of communication, like via a fax or a pigeon.

‘Get a job in cyber’

Surprise, I am not a cyber security expert. You can read more about the libsodium encryption method underlying {encryptedRmd} if you’re wondering how it works, etc.

As noted by Dirk in the {encryptedRmd} README: use at your own risk.

For me, the outlined approach does what I need and does it painlessly. I have no intention to share actually-sensitive information via this method, so I have little to worry about. I’m Smiley as George Smiley in Tinker Tailor Soldier Spy.4


Session info
Last rendered: 2023-07-21 18:39:35 BST
R version 4.3.1 (2023-06-16)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.2.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/London
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] htmlwidgets_1.6.2 compiler_4.3.1    fastmap_1.1.1     cli_3.6.1        
 [5] tools_4.3.1       htmltools_0.5.5   rstudioapi_0.15.0 yaml_2.3.7       
 [9] rmarkdown_2.23    knitr_1.43.1      jsonlite_1.8.7    xfun_0.39        
[13] digest_0.6.33     rlang_1.1.1       evaluate_0.21    


  1. Starring the adjective-name actors Strong Mark, Hurt John and Oldman Gary.↩︎

  2. All too often I ‘discover’ a cool-sounding project on GitHub only to find I’ve already starred it. Partly I’m forgetful. Partly it’s because I’ve starred so many things. (I think this phenomenon should have a word. Possibly a compound German word. Or like ‘brown dwarf’, because it’s a celestial object that had potential to begin fusion but didn’t quite make it? Rubbish analogy.) The {encryptedRmd} case is especially amusing because apparently I gave a thumbs-up to an issue in the repo posted by my colleague Duncan.↩︎

  3. You could simplify this by renaming your encrypted output file to index.html, which means you can refer to it with a shortened URL in the form user.github.io/repo-name/.↩︎

  4. Spoiler: he didn’t actually smile very much.↩︎