# Hosting Markdown Files on Apache John Simpson `` 2016-06-23 Last updated 2020-02-10 - updated this page with the *actual* `markdown.cgi` script which is being used on the site. This documents how to host Markdown files directly on an Apache web server, running on CentOS 6. The software which actually renders Markdown into HTML is a Ruby "gem" called [redcarpet](https://rubygems.org/gems/redcarpet/). This is available from the EPEL repo. ## EPEL Repo The [EPEL repository](https://fedoraproject.org/wiki/EPEL) contains a collection of Fedora RPMs, packaged for use with RedHat Enterprise (or CentOS.) If the server doesn't already have the EPEL repo installed, install it by running one of the following commands: * CentOS/RHEL 7: `rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm` * CentOS/RHEL 6: `rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm` * CentOS/RHEL 5: `rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-5.noarch.rpm` ## Install the redcarpet gem Install the `rubygem-redcarpet` package. If they aren't already installed, this will install `ruby` and `ruby-libs` (from the CentOS base repo) as well. ``` yum install rubygem-redcarpet ``` ## Create a markdown directory We will be creating a CGI script which uses `redcarpet` to perform the rendering. This script needs to be in a location which is accessible from the web site. On my own server I created a `/markdown/` directory in the root of the web site, like so: ``` mkdir -m 0755 /var/www/website/markdown ``` ## Create `markdown.cgi` In the new directory, create `markdown.cgi`. It should be owned by root, with permissions `0755`, and the following contents: ``` #!/usr/bin/ruby # # markdown.cgi # John Simpson 2016-06-23 # # 2016-06-28 jms1 - added header with source download link at the top of the # rendered page # # 2016-07-07 jms1 - tweaked the format of the source link # # 2018-07-18 jms1 - Removed the "topmenu" stuff. I've gotta be honest, I'm # not sure what I was thinking there. # ############################################################################### # # Copyright (C) 2016-2018 John Simpson. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, either version 2 # or version 3, as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/licenses/gpl.txt # ############################################################################### require 'redcarpet' renderer = Redcarpet::Render::HTML.new( :with_toc_data => true , :xhtml => true ) markdown = Redcarpet::Markdown.new( renderer , :autolink => true , :strikethrough => true , :superscript => true , :underline => true , :highlight => true , :fenced_code_blocks => true , :tables => true ) ############################################################################### # Figure out what we're doing infile = ENV['PATH_TRANSLATED'] # full disk path to what was requested root = ENV['DOCUMENT_ROOT'] # web site's document root ######################################## # If the request had "/src" at the end, remove it # and set a flag to show source instead of rendered HTML if ( infile.match( /\/src$/ ) ) then show_src = true infile = infile.gsub( /\/src$/ , '' ) else show_src = false end ######################################## # Other bits of text we'll probably need my_url = infile.gsub( /^#{root}/ , '' ) title = infile.gsub( /^.*\// , '' ) ######################################## # Read the file from the disk text = File.read( infile ) ############################################################################### # # Output if ( show_src ) then ######################################## # Send the plain text to the browser print "Content-Type: text/plain\n\n#{text}" else ######################################## # Send the rendered output to the browser md = markdown.render( text ) print( <<-EOF ) Content-Type: text/html #{title} #{md} EOF end ``` ## Create `markdown.css` This is the CSS stylesheet which controls the visual appearnance of the rendered HTML. The stylesheet I use is below. It's a modified version of one of the samples that came with [Marked2](http://marked2app.com/) (which I highly recommend as a way to preview changes, as well as render PDFs of finished documents.) ``` body { -webkit-font-smoothing: antialiased ; font: normal .8764em/1.5em Arial,Verdana,sans-serif ; } html>body { font-size: 13px ; } li { font-size: 110% ; } li li { font-size: 100% ; } li p { font-size: 100% ; margin: .5em 0 ; } h1 { color: #000 ; font-size: 2.2857em ; line-height: .6563em ; margin: .6563em 0 ; border-top: 3px solid ; } h2 { color: #111 ; font-size: 1.7143em ; line-height: .875em ; margin: .875em 0 ; border-top: 1px solid ; } h3 { color: #111 ; font-size: 1.5em ; line-height: 1em ; margin: 1em 0 ; } h4 { color: #111 ; font-size: 1.2857em ; line-height: 1.1667em ; margin: 1.1667em 0 ; } h5 { color: #111 ; font-size: 1.15em ; line-height: 1.3em ; margin: 1.3em 0 ; } h6 { font-size: 1em ; line-height: 1.5em ; margin: 1.5em 0 ; } body,p,td,div { color: #111 ; font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif ; word-wrap: break-word ; } h1,h2,h3,h4,h5,h6 { line-height: 1.5em ; } a { -webkit-transition: color .2s ease-in-out ; -moz-transition: color .2s ease-in-out ; -o-transition: color .2s ease-in-out ; -ms-transition: color .2s ease-in-out ; transition: color .2s ease-in-out ; color: #0d6ea1 ; text-decoration: none ; } a:hover { color: #3593d9 ; } .footnote { color: #0d6ea1 ; font-size: .8em ; vertical-align: super ; } #wrapper img { max-width: 100% ; height: auto ; } dd { margin-bottom: 1em ; } li > p:first-child { margin: 0 ; } ul ul,ul ol { margin-bottom: .4em ; } caption,col,colgroup,table,tbody,td,tfoot,th,thead,tr { border-spacing: 0 ; } table { border: 1px solid rgba(0,0,0,0.25) ; border-collapse: collapse ; display: table ; empty-cells: hide ; margin: -1px 0 23px ; padding: 0 ; table-layout: fixed ; } caption { display: table-caption ; font-weight: 700 ; } col { display: table-column ; } colgroup { display: table-column-group ; } tbody { display: table-row-group ; } tfoot { display: table-footer-group ; } thead { display: table-header-group ; } td,th { display: table-cell ; } tr { display: table-row ; } table th,table td { font-size: 1.1em ; line-height: 23px ; padding: 0 1em ; } table thead { background: rgba(0,0,0,0.15) ; border: 1px solid rgba(0,0,0,0.15) ; border-bottom: 1px solid rgba(0,0,0,0.2) ; } table tbody { background: rgba(0,0,0,0.05) ; } table tfoot { background: rgba(0,0,0,0.15) ; border: 1px solid rgba(0,0,0,0.15) ; border-top: 1px solid rgba(0,0,0,0.2) ; } figure { display: inline-block ; position: relative ; margin: 1em 0 2em ; } figcaption { font-style: italic ; text-align: center ; background: white ; color: #666 ; position: absolute ; left: 0 ; bottom: -24px ; width: 98% ; padding: 1% ; -webkit-transition: all .2s ease-in-out ; -moz-transition: all .2s ease-in-out ; -o-transition: all .2s ease-in-out ; -ms-transition: all .2s ease-in-out ; transition: all .2s ease-in-out ; } figure:hover>figcaption { /* background: rgba(0,0,0,1) */ } .poetry pre { display: block ; font-family: Georgia, Garamond, serif !important ; font-size: 110% !important ; font-style: italic ; line-height: 1.6em ; margin-left: 1em ; } .poetry pre code { font-family: Georgia, Garamond, serif !important ; } blockquote p { font-size: 110% ; font-style: italic ; line-height: 1.6em ; } sup,sub,a.footnote { font-size: 1.4ex ; height: 0 ; line-height: 1 ; position: relative ; vertical-align: super ; } sub { vertical-align: sub ; top: -1px ; } p,h5 { font-size: 1.1429em ; line-height: 1.3125em ; margin: 1.3125em 0 ; } dt,th { font-weight: 700 ; } pre { border: 1px solid ; padding: 2px ; } table tr:nth-child(odd),table th:nth-child(odd),table td:nth-child(odd) { background: rgba(255,255,255,0.06) ; } table tr:nth-child(even),table td:nth-child(even) { background: rgba(200,200,200,0.25) ; } @media print { body { overflow: auto ; } img,pre,blockquote,table,figure,p { page-break-inside: avoid ; } #wrapper { background: #fff ; color: #303030 ; font-size: 85% ; padding: 10px ; position: relative ; text-indent: 0 ; } } @media screen { .inverted #wrapper,.inverted { background: rgba(37, 42, 42, 1) ; } .inverted hr { border-color: rgba(51, 63, 64, 1) !important ; } .inverted p,.inverted td,.inverted li,.inverted h1,.inverted h2,.inverted h3,.inverted h4,.inverted h5,.inverted h6,.inverted pre,.inverted code,.inverted th,.inverted .math,.inverted caption,.inverted dd,.inverted dt { color: #eee !important ; } .inverted table tr:nth-child(odd),.inverted table th:nth-child(odd),.inverted table td:nth-child(odd) { background: none ; } .inverted a { color: rgba(172, 209, 213, 1) ; } #wrapper { padding: 20px ; } ::selection { background: rgba(157, 193, 200,.5) ; } h1::selection { background-color: rgba(45, 156, 208, .3) ; } h2::selection { background-color: rgba(90, 182, 224, .3) ; } h3::selection,h4::selection,h5::selection,h6::selection,li::selection,ol::selection { background-color: rgba(133, 201, 232, .3) ; } code::selection { background-color: rgba(0,0,0,.7) ; color: #eee ; } code span::selection { background-color: rgba(0,0,0,.7) !important ; color: #eee !important ; } a::selection { background-color: rgba(255, 230, 102,.2) ; } .inverted a::selection { background-color: rgba(255, 230, 102,.6) ; } td::selection,th::selection,caption::selection { background-color: rgba(180, 237, 95, .5) ; } } ``` ## Configuration In the DocumentRoot's `.htaccess` file, add the following: ``` Action markdown /markdown/markdown.cgi AddHandler markdown .md DirectoryIndex index.cgi index.shtml index.html index.md ``` For the `DirectoryIndex` line, the idea is to add `index.md` to the list. I'm not sure if there's a higher-level `Allow` option that needs to be set in order for these directives to be processed, I normally use `AllowOverride all` for web sites, especially for sites where authoring access is limited.