How to redirect http to https

I’ve asked about related things before – Custom domains, www vs root, http vs https - #22 by dreev – but I have a small specific question:

Can I automatically redirect from the insecure, non-SSL, http version of my project’s URL to the secure https version?

I’ll need to figure this out for custom domains but maybe it’s easier to start with the case of redirecting from http://foo.glitch.me to https://foo.glitch.me

Thanks for any insights!

PS: For quick reference, here’s what I’m now doing for static sites, thanks to @WhiteNemo:

<script>
if (window.location.protocol === 'http:')
  window.location.replace('https'+window.location.href.slice(4))
</script>
2 Likes

I don’t think this is an ideal solution but it seems to work to stick the following in the html header:

<script>
if (location.protocol !== 'https:') {
  location.replace(`https:${location.href.substring(location.protocol.length)}`)
}
</script>

I’m using this:

if (window.location.protocol === "http:")
  window.location.protocol = "https:";
1 Like

Well that’s much simpler! I guess “http” and “https” are the only protocols one will ever see these days so “if http then make it https” is quite sufficient. Thank you!

UPDATE: Wait, see below. The above breaks the back button on some browsers.

Glitch does not have any setting to automatically redirect from http to https, so you will have to set it up by yourself. How you would set it up depends on your project, but here are some of the most common on Glitch:

No backend (static website):
There is no good solution if you do not have a backend. The only thing you can do is issuing a redirect from the front end using location.replace like below (assigning to location.protocol will not allow your users to use the back button to go back). However, it does not really give any protection; the fact that your page is rendered via http is all an attacker needs.

const {href, protocol} = window.location
if (protocol === 'http:') {
  window.location.replace(`https${href.slice(4)}`)
}

Node:
Since there are many web frameworks for Node all doing things differently, I will show three ways to do it; without external dependencies, with Express, and with Fastify.

//Without dependencies

const {PORT} = process.env

const {createServer} = require('http')

const secureConnection = ({headers, url}, response) => {
  const protocols = headers['x-forwarded-proto'].split(',')
  const secure = protocols[0] === 'https'
  
  if (!secure) {
    const newURL = `https://${headers.host}${url}`
    response.writeHead(308, {'Location': newURL}).end()
  }
}

const requestListener = (request, response) => {
  secureConnection(request, response)
  
  // Put your other rutes here; eg.
  response.end('Hello World!')
}

const server = createServer(requestListener)

server.listen(PORT)
// With Express

const {PORT} = process.env

const {createServer} = require('http')
const express = require('express')

const requestListener = express()

requestListener.set('trust proxy', [
  '127.0.0.1',
  '10.0.0.0/8'
])

requestListener.use(({secure, hostname, url}, response, next) => {
  if (!secure) {
    return response.redirect(308, `https://${hostname}${url}`)
  }
  
  next()
})

// Put your other rutes here; eg.
requestListener.get('/', (request, response) => {
  response.send('Hello World!')
})

const server = createServer(requestListener)

server.listen(PORT)
// With Fastify

const {PORT} = process.env

const {createServer} = require('http')
const fastify = require('fastify')

const server = createServer()

const trustProxy = [
  '127.0.0.1',
  '10.0.0.0/8'
]

const serverFactory = (handler, options) => {
  server.on('request', (request, response) => {
    handler(request, response)
  })
  
  return server
}

const requestListener = fastify({
  trustProxy,
  serverFactory
})

requestListener.decorateRequest('secure', false)

requestListener.addHook('onRequest', (request, reply, done) => {
  // Do not use `request.protocol` (will always return `http`)
  // Fastify does not parse multi-value `x-forwarded-proto` correctly
  const protocols = request.headers['x-forwarded-proto'].split(',')
  request.secure = protocols[0] === 'https'
  
  done()
})

requestListener.addHook('onRequest', ({secure, hostname, url}, reply, done) => {
  if (!secure) {
    return reply.redirect(308, `https://${hostname}${url}`)
  }
  
  done()
})

// Put your other rutes here; eg.
requestListener.get('/', (request, reply) => {
  reply.send('Hello World!')
})

requestListener.listen(PORT)

PHP (LAMP):
Even though it is not officially supported on Glitch, it is still a quite popular backend language / stack outside of Glitch. If you do not know how to use PHP on Glitch, I have created a LAMP starter project that should make it easy to get started.

# Using .htaccess file

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=308,QSA,L]
3 Likes

Ah, thank you! For the no-backend case, is there any advantage to your version over @kael’s above?

Using location.protocol will break back-button functionality.
Lets say your user is on http://othersite.com and clicks on a link to your site (http://yoursite.com). This will cause the JavaScript to run, redirecting the user to https://yoursite.com. The user decides to use the back button, because he wants to return to http://othersite.com, however, because you did not use location.replace, he will only go back to http://yoursite.com, causing another redirect back to https://yoursite.com.
This is not really a good user expierience, and may cause him not to come back again.

2 Likes

The following project is currently using @kael’s solution and it seems to not break the back button: http://oaks.glitch.me vs https://oaks.glitch.me

Maybe my particular browser is being clever about this (violating the anti-Postel principle perhaps?) but I should not rely on this?

Some browsers may be clever enough to detect it, but since it is not part of the specs I personally would not rely on all browser doing it, or even keep doing it if they do it now.

3 Likes

Sure enough, Chrome is stupidly being clever and masking the problem. In Firefox, @kael’s solution breaks the back button. Confirmed that your solution works! Thank you again!

3 Likes