Chuyển đến nội dung

Triển khai với Apache ​

Lưu trữ BentoPDF bằng Apache HTTP Server.

Yêu cầu ​

  • Apache 2.4+
  • Đã bật mod_rewrite
  • Chứng chỉ SSL (khuyến nghị)

Bước 1: Build dự án ​

bash
git clone https://github.com/alam00000/bentopdf.git
cd bentopdf
npm install
npm run build

Để tùy chỉnh thương hiệu, đặt các biến môi trường trước khi build:

bash
VITE_BRAND_NAME="AcmePDF" VITE_BRAND_LOGO="images/acme-logo.svg" npm run build

Bước 2: Sao chép tệp ​

bash
sudo mkdir -p /var/www/bentopdf
sudo cp -r dist/* /var/www/bentopdf/
sudo chown -R www-data:www-data /var/www/bentopdf

Bước 3: Cấu hình Apache ​

Tạo /etc/apache2/sites-available/bentopdf.conf:

apache
<VirtualHost *:80>
    ServerName your-domain.com
    Redirect permanent / https://your-domain.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName your-domain.com
    DocumentRoot /var/www/bentopdf

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem

    <Directory /var/www/bentopdf>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # WASM MIME type
    AddType application/wasm .wasm

    # Prevent double-compression of pre-compressed files
    SetEnvIfNoCase Request_URI "\.gz$" no-gzip

    # Compression
    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json application/wasm
    </IfModule>

    # Cache headers
    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType application/javascript "access plus 1 year"
        ExpiresByType text/css "access plus 1 year"
        ExpiresByType application/wasm "access plus 1 year"
        ExpiresByType image/png "access plus 1 year"
        ExpiresByType image/svg+xml "access plus 1 year"
    </IfModule>

    # Required headers for SharedArrayBuffer (LibreOffice WASM)
    Header always set Cross-Origin-Embedder-Policy "require-corp"
    Header always set Cross-Origin-Opener-Policy "same-origin"
    Header always set Cross-Origin-Resource-Policy "cross-origin"

    # Security headers
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"

    # Pre-compressed LibreOffice WASM files
    <FilesMatch "soffice\.wasm\.gz$">
        ForceType application/wasm
        Header set Content-Encoding "gzip"
    </FilesMatch>
    <FilesMatch "soffice\.data\.gz$">
        ForceType application/octet-stream
        Header set Content-Encoding "gzip"
    </FilesMatch>
</VirtualHost>

Bước 4: .htaccess cho định tuyến ​

Tạo /var/www/bentopdf/.htaccess:

apache
RewriteEngine On
RewriteBase /

# Existing files/dirs - serve directly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# ============================================
# LANGUAGE ROUTES
# ============================================
# Supported languages: de, es, zh, zh-TW, vi, it, id, tr, fr, pt
# English has no prefix - served from root

# English prefix redirects to root
RewriteRule ^en/?$ / [R=301,L]
RewriteRule ^en/(.+)$ /$1 [R=301,L]

# Language prefix root (e.g., /de/ -> /de/index.html)
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /$1/index.html [L]

# Language prefix with path (e.g., /de/merge-pdf -> /de/merge-pdf.html)
RewriteCond %{DOCUMENT_ROOT}/$1/$2.html -f
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /$1/$2.html [L]

# ============================================
# ADD .HTML EXTENSION (ROOT LEVEL)
# ============================================
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^/]+)$ $1.html [L]

ErrorDocument 404 /404.html

Bước 5: Bật các module cần thiết ​

bash
sudo a2enmod rewrite
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod expires
sudo a2enmod deflate

Bước 6: Bật trang web ​

bash
sudo a2ensite bentopdf.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Triển khai trong thư mục con ​

Để lưu trữ tại /pdf/:

  1. Build với base URL:
bash
BASE_URL=/pdf/ npm run build
  1. Cập nhật .htaccess:
apache
RewriteEngine On
RewriteBase /pdf/

# Existing files/dirs - serve directly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# Language routes
RewriteRule ^en/?$ /pdf/ [R=301,L]
RewriteRule ^en/(.+)$ /pdf/$1 [R=301,L]

RewriteCond %{DOCUMENT_ROOT}/pdf/$1/index.html -f
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/?$ /pdf/$1/index.html [L]

RewriteCond %{DOCUMENT_ROOT}/pdf/$1/$2.html -f
RewriteRule ^(de|es|zh|zh-TW|vi|it|id|tr|fr|pt)/([^/]+)/?$ /pdf/$1/$2.html [L]

# Root level .html extension
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^([^/]+)$ $1.html [L]

ErrorDocument 404 /pdf/404.html

Khắc phục sự cố ​

Lỗi WASM 404 ​

Đảm bảo đã cấu hình MIME type:

apache
AddType application/wasm .wasm

Ký PDF hoặc Form Filler hiển thị trình xem trống (lỗi MIME .mjs) ​

Nếu console của trình duyệt hiển thị:

Failed to load module script: The server responded with a non-JavaScript
MIME type of "application/octet-stream". Strict MIME type checking is
enforced for module scripts per HTML spec.

...và yêu cầu bị lỗi có đuôi .mjs, cấu hình Apache của bạn đang thiếu ánh xạ .mjs → application/javascript. Trình xem PDF đi kèm dùng các tệp ES module có đuôi .mjs, và mime.types mặc định của Apache không có sẵn đuôi này.

Thêm vào VirtualHost hoặc .htaccess:

apache
AddType application/javascript .mjs

Sau đó tải lại Apache:

bash
sudo apache2ctl configtest && sudo systemctl reload apache2

Để xác nhận: trong DevTools của trình duyệt → thẻ Network, tìm yêu cầu .mjs bị lỗi và kiểm tra header phản hồi Content-Type. Giờ nó phải là application/javascript, không phải application/octet-stream.

Rewrite không hoạt động ​

Kiểm tra mod_rewrite đã được bật:

bash
sudo a2enmod rewrite

Chuyển Word/ODT/Excel sang PDF không hoạt động ​

LibreOffice WASM cần SharedArrayBuffer, và điều này đòi hỏi các header sau:

apache
Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy "same-origin"

Nó cũng cần một ngữ cảnh bảo mật (secure context). http://localhost dùng được để thử nghiệm cục bộ, nhưng http://192.168.x.x hoặc các IP LAN khác thường cần HTTPS. Nếu các header đã có nhưng window.crossOriginIsolated vẫn là false, hãy kiểm tra xem trang có đang được mở qua HTTP thường trên một origin không phải loopback hay không.

Các tệp .wasm.gz và .data.gz nén sẵn cũng cần Content-Encoding đúng:

apache
<FilesMatch "soffice\.wasm\.gz$">
    ForceType application/wasm
    Header set Content-Encoding "gzip"
</FilesMatch>
<FilesMatch "soffice\.data\.gz$">
    ForceType application/octet-stream
    Header set Content-Encoding "gzip"
</FilesMatch>

Đảm bảo mod_headers đã được bật: sudo a2enmod headers

Bị từ chối quyền truy cập ​

bash
sudo chown -R www-data:www-data /var/www/bentopdf
sudo chmod -R 755 /var/www/bentopdf

Cấp phép kép theo AGPL-3.0 và Giấy phép thương mại.