Angular SSR Applikation in nginx mit gzip_static Kompression

Ich habe eine Angular SSR Anwendung, die mit express serverseitig gerendert wird und über nginx über einen reverse proxy mit proxy_pass ausgeliefert wird. Beim Build erzeuge ich für jede generierte JavaScript Datei gleichzeitig auch eine gzip Datei im gleichen Verzeichnis. Ich habe versucht für nginx die gzip_static Direktive zu aktivieren, allerdings ohne Erfolg. Die Dateien werden weiterhin ohne gzip Compression ausgeliefert:

server {
    ...
    gzip_static on;
    gzip_proxied any;
    gzip_vary on;
    gunzip on;
    gzip_types application/javascript
    ...
}

Wie kann ich gzip_static für meine Angular Anwendung verwenden?

Noch keine Stimmen abgegeben
1 Kommentar
  • Das wird wohl nicht gehen. In nginx mit proxy_pass bringt die Konfiguration für gzip_static nichts: That is, gzip_static is only expected to work when nginx is about to return regular files. It's not expected to do anything when all requests are...
    von TheSupervisor am 17 Feb. 2023
  • 26 Feb. 2023

    Die Konfiguration von Express und SSR-Angular, falls die JavaScript Dateien im Build schon gzip-t werden und sich im gleichen Verzeichnis wie die eigentliche JavaScript Datei befinden (Auszug aus server.ts ):

    export function app(): express.Express {
      const server = express();
      const distFolder = join(process.cwd(), 'dist/browser');
    
      // Hier folgt die standard-Konfiguration  von Angular + SSR, wie sie von Angular Cli automatisch erzeugt wird, ...
    
     server.get('*.js', (req, res, next) => {
        if (!existsSync(`${distFolder}${req.url}.gz`) || 
            req.headers['x-no-compression'] || 
            !req.header('Accept-Encoding').includes('gzip')) {
          return next();
        }
        req.url = `${req.url}.gz`;
        res.set({
          'Content-Type': 'text/javascript',
          'Content-Encoding': 'gzip',
          'Vary': 'Accept-Encoding',
          'Cache-Control': 'max-age=31536000'
        });
        next();
      });
    
      // ... und noch mehr Konfiguration
    
    }
    

    Eigentlich ist es relativ selbsterklärend: Kommt ein GET Request für eine JavaScript *.js Datei, so werden erst einmal paar Bedingungen geprüft ob die Datei ausgeliefert werden kann und falls sie zutreffen, wird die Datei mit entsprechenden Headern zurückgegeben.

    • Mit existsSync(`${distFolder}${req.url}.gz` wird geprüft ob die gzip-te Datei existiert (existsSync aus dem "fs" modul).
    • Über den Accept-Encoding Request-Header prüfen wir, ob der Browser die gzip Compression überhaupt unterstützt (das machen aber alle modernen Browser).
    • Wir prüfen noch ob der Header req.headers['x-no-compression'] gesetzt ist: x-no-compression ist zwar kein Standard-Header, der Header wird aber auch in dem express-static-gzip Projekt verwendet und es macht auch Sinn, für den Fall des Falles eine Möglichkeit zu haben, über die man die Kompression ausschalten kann.

    Noch kurz zu den Response-Headern:

    • Ich denke "Content-Type" und "Content-Encoding" müssten klar sein
    • Vary mit Accept-Encoding: dieser Header weist Caching-Server und Proxys an, separate Kopien der komprimierten und unkomprimierten Versionen der JavaScript-Dateien zu speichern, basierend auf dem „Accept-Encoding“-Header des Clients. Dadurch wird sichergestellt, dass der Server in Abhängigkeit von den Fähigkeit des Clients, den Inhalt zu dekomprimieren, die geeignete Version des Inhalts bereitstellt. Allerdings, soweit ich das verstehe, müssten wir bei Cache-Control den Flag public setzen. Sonst dürfen die Dateien nicht auf Proxys/Caching-Servern gespeichert werden.
    • Hier bin ich mir nicht sicher (auch in Verbindung mit dem "Vary" Header) ob Cache-Control: max-age=31536000, immutable, public nicht besser wäre: Da die generierten JS-Dateien im Namen einen Cache-Breaker (oder Cache-Buster) enthalten, sollte es kein Problem sein den Header eben auf "public" und "immutable" zu setzen.
    Noch keine Stimmen abgegeben
    Noch keine Kommentare