http-redirecthttpsdenooak

Deno Oak web-server - force redirect http to https


Is there a way to catch all http traffic and redirect it to https using Deno / Deno and Oak while also serving https?

I have a simple Deno web-server using the Oak middleware. I have it set up for https and everything works when connecting using https. Ports, firewall, certificate, etc. - all good.

However, if I try connecting using http I get the following error [uncaught application error]: Http - error writing a body to connection: handshake not complete: handshake not complete.

I have no interest in serving http. If you can't use https, you can't access my site. As such, I would like to redirect all http traffic to the https address.

In the past I have used Nginx catch the http traffic and redirect it, but I have a clean install for a new project and wanted to see if I could do it all in Deno.

I have tried adding a middleware stage httpRedirect to the route which asks if the request was coming in on http or https, if using http then redirect the user to the same site but via https, or if via https pass on to the next stage, however, it fails before it gets to my function.

Note: this is currently running on port 7700 and I use IPTABLES to forward 80 and 443 to 7700.

import { Application } from 'https://deno.land/x/oak@v10.5.1/mod.ts';
import { Router } from 'https://deno.land/x/oak@v10.5.1/mod.ts';

const HOST = '';  // Removed
const PORT = '7700';
const SECURE = true;
const CERT_FILE = '/fullchain.pem';  // Removed for stackoverflow
const KEY_FILE = '/privkey.pem';  // Removed for stackoverflow

const app = new Application();

const router = new Router();

// respondWith is a simple function that reads a file and responds with the contents.
router.get('/', httpRedirect, async (ctx) => await respondWith(ctx, './index.html'));

app.use(router.routes());
app.use(router.allowedMethods());

let appOptions = {};
if (SECURE) {
    appOptions = {port: PORT, hostname: HOST, secure: true, certFile: CERT_FILE, keyFile: KEY_FILE};
} else {
    appOptions = {port: PORT, hostname: HOST};
}

console.log(`Listening on port ${PORT} ...`);
await app.listen(appOptions);
const httpRedirect = async (ctx: Context, next:any) => {
    if (ctx.request.secure) {
        await next();
    } else {
        // redirect to https
    }
}

Solution

  • Thanks to @jsejcksn 's advice I have a solution.

    I didn't use Oak, but I did keep it within Deno.

    I added a second server using Deno's standard http package that acts only as a redirect. Obviously this needs to be on its own port (I used 7701). I then used IPTABLES to forward https requests (port 443) to my Oak server, and forward http request (port 80) to my standard http (redirect) server.

    It's not as clean as I would have liked, but it works.

    Added code:

    function httpRedirect(req: Request) {
        const redirectUrl = new URL(req.url);
        redirectUrl.protocol = 'https:'
        redirectUrl.port = '443';
        // console.log(redirectUrl);
        return Response.redirect(redirectUrl, 301);
    }
    
    console.log(`HTTP redirect running on port ${HTTP_PORT} ...`);
    serve(httpRedirect, { port: HTTP_PORT });