How to implement temporary file download URLs with expiration in Express.js

Hello everyone!

I’m working on a web application using Express.js and need to implement a feature where users can download files through URLs that automatically expire after a specific time period.

The basic idea is:

  • User requests a file download
  • Server generates a temporary URL with expiration timestamp
  • URL becomes invalid after the set time limit

I’ve been trying to figure this out for several days but can’t seem to get it working properly. The main challenges I’m facing are:

  1. How to generate secure temporary tokens
  2. How to validate expiration on the server side
  3. How to handle the frontend request properly

Here’s what I’ve attempted so far:

// My basic attempt at token generation
function generateToken(userId, fileName) {
    const timestamp = Date.now();
    const payload = {
        user: userId,
        file: fileName,
        created: timestamp
    };
    return btoa(JSON.stringify(payload));
}

// Route handler attempt
app.get('/download/:token', (req, res) => {
    // Not sure how to validate expiration here
    const token = req.params.token;
    // Need help with validation logic
});

Could someone share a complete working example showing both the server-side token generation/validation and the client-side implementation? Any guidance would be greatly appreciated!

don’t use btoa - it’s not secure! go with crypto.randomBytes() and store your tokens in redis or memory with ttl. try const token = crypto.randomBytes(32).toString('hex'), save it with an expiry time, then check it against your stored data in the download route.

Interesting approaches! But I’m curious - what if someone shares these download links? Are you adding any user validation to stop unauthorized access? Also @ZoeString42, what kind of files are you serving and how sensitive are they?

Had this exact problem last year - JWT tokens are perfect for this. You can bake the expiration right into the token, so no server-side storage needed. Here’s what I did:

const jwt = require('jsonwebtoken');

// Generate download URL
app.post('/generate-download', (req, res) => {
    const token = jwt.sign({
        userId: req.user.id,
        fileName: req.body.fileName,
        exp: Math.floor(Date.now() / 1000) + (60 * 15) // 15 minutes
    }, process.env.JWT_SECRET);
    
    res.json({ downloadUrl: `/download/${token}` });
});

// Download handler
app.get('/download/:token', (req, res) => {
    try {
        const decoded = jwt.verify(req.params.token, process.env.JWT_SECRET);
        const filePath = path.join(__dirname, 'files', decoded.fileName);
        res.download(filePath);
    } catch (error) {
        res.status(401).json({ error: 'Token expired or invalid' });
    }
});

Best part? JWT handles expiration checks automatically and you don’t need any extra storage for managing tokens.