diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 0401832..c0fa147 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -15,6 +15,8 @@ jobs: if: startsWith(github.ref, 'refs/tags/') permissions: contents: write + env: + HUSKY: 0 steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml index 0b1435b..8d261dc 100644 --- a/.github/workflows/sponsors.yml +++ b/.github/workflows/sponsors.yml @@ -1,7 +1,5 @@ name: Update Sponsors on: - schedule: - - cron: '0 0 * * *' # Daily workflow_dispatch: jobs: update: diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index b0f91c5..c81d723 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -27,6 +27,7 @@ concurrency: env: SIMPLE_MODE: ${{ vars.SIMPLE_MODE }} BASE_URL: ${{ vars.BASE_URL }}/ + HUSKY: 0 jobs: # Single deploy job since we're just deploying diff --git a/.gitignore b/.gitignore index 3527f29..c4f76c1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ dist dist-ssr *.local .npm-cache +.env +.env.production +.env.local # Editor directories and files .vscode/* diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/404.html b/404.html index ba1c6b9..5d2924d 100644 --- a/404.html +++ b/404.html @@ -45,7 +45,7 @@
- © 2025 BentoPDF. All rights reserved. + © 2026 BentoPDF. All rights reserved.
Version diff --git a/Dockerfile b/Dockerfile index de59634..defc4d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ COPY vendor ./vendor +ENV HUSKY=0 RUN npm ci COPY . . @@ -36,8 +37,12 @@ LABEL org.opencontainers.image.url="https://github.com/alam00000/bentopdf" # global arg to local arg ARG BASE_URL +# Set this to "true" to disable Nginx listening on IPv6 +ENV DISABLE_IPV6=false + COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html${BASE_URL%/} COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf +COPY --chown=nginx:nginx --chmod=755 nginx-ipv6.sh /docker-entrypoint.d/99-disable-ipv6.sh RUN mkdir -p /etc/nginx/tmp && chown -R nginx:nginx /etc/nginx/tmp EXPOSE 8080 diff --git a/README.md b/README.md index 2b418d1..379c25f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -
- © 2025 BentoPDF. All rights reserved. + © 2026 BentoPDF. All rights reserved.
Version
diff --git a/cloudflare/cors-proxy-worker.js b/cloudflare/cors-proxy-worker.js
new file mode 100644
index 0000000..b9ba458
--- /dev/null
+++ b/cloudflare/cors-proxy-worker.js
@@ -0,0 +1,351 @@
+/**
+ * BentoPDF CORS Proxy Worker
+ *
+ * This Cloudflare Worker proxies certificate requests for the digital signing tool.
+ * It fetches certificates from external CAs that don't have CORS headers enabled
+ * and returns them with proper CORS headers.
+ *
+ *
+ * Deploy: npx wrangler deploy
+ *
+ * Required Environment Variables (set in wrangler.toml or Cloudflare dashboard):
+ * - PROXY_SECRET: Shared secret for HMAC signature verification
+ */
+
+const ALLOWED_PATTERNS = [
+ /\.crt$/i,
+ /\.cer$/i,
+ /\.pem$/i,
+ /\/certs\//i,
+ /\/ocsp/i,
+ /\/crl/i,
+ /caIssuers/i,
+];
+
+const ALLOWED_ORIGINS = [
+ 'https://www.bentopdf.com',
+ 'https://bentopdf.com',
+];
+
+const BLOCKED_DOMAINS = [
+ 'localhost',
+ '127.0.0.1',
+ '0.0.0.0',
+];
+
+
+const MAX_TIMESTAMP_AGE_MS = 5 * 60 * 1000;
+
+const RATE_LIMIT_MAX_REQUESTS = 60;
+const RATE_LIMIT_WINDOW_MS = 60 * 1000;
+
+const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
+
+async function verifySignature(message, signature, secret) {
+ try {
+ const encoder = new TextEncoder();
+ const key = await crypto.subtle.importKey(
+ 'raw',
+ encoder.encode(secret),
+ { name: 'HMAC', hash: 'SHA-256' },
+ false,
+ ['verify']
+ );
+
+ const signatureBytes = new Uint8Array(
+ signature.match(/.{1,2}/g).map(byte => parseInt(byte, 16))
+ );
+
+ return await crypto.subtle.verify(
+ 'HMAC',
+ key,
+ signatureBytes,
+ encoder.encode(message)
+ );
+ } catch (e) {
+ console.error('Signature verification error:', e);
+ return false;
+ }
+}
+
+async function generateSignature(message, secret) {
+ const encoder = new TextEncoder();
+ const key = await crypto.subtle.importKey(
+ 'raw',
+ encoder.encode(secret),
+ { name: 'HMAC', hash: 'SHA-256' },
+ false,
+ ['sign']
+ );
+
+ const signature = await crypto.subtle.sign(
+ 'HMAC',
+ key,
+ encoder.encode(message)
+ );
+
+ return Array.from(new Uint8Array(signature))
+ .map(b => b.toString(16).padStart(2, '0'))
+ .join('');
+}
+
+function isAllowedOrigin(origin) {
+ if (!origin) return false;
+ return ALLOWED_ORIGINS.some(allowed => origin.startsWith(allowed.replace(/\/$/, '')));
+}
+
+function isValidCertificateUrl(urlString) {
+ try {
+ const url = new URL(urlString);
+
+ if (!['http:', 'https:'].includes(url.protocol)) {
+ return false;
+ }
+
+ if (BLOCKED_DOMAINS.some(domain => url.hostname.includes(domain))) {
+ return false;
+ }
+
+ const hostname = url.hostname;
+ if (/^10\./.test(hostname) ||
+ /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname) ||
+ /^192\.168\./.test(hostname)) {
+ return false;
+ }
+
+ return ALLOWED_PATTERNS.some(pattern => pattern.test(urlString));
+ } catch {
+ return false;
+ }
+}
+
+function corsHeaders(origin) {
+ return {
+ 'Access-Control-Allow-Origin': origin || '*',
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type',
+ 'Access-Control-Max-Age': '86400',
+ };
+}
+
+function handleOptions(request) {
+ const origin = request.headers.get('Origin');
+ return new Response(null, {
+ status: 204,
+ headers: corsHeaders(origin),
+ });
+}
+
+export default {
+ async fetch(request, env, ctx) {
+ const url = new URL(request.url);
+ const origin = request.headers.get('Origin');
+
+ if (request.method === 'OPTIONS') {
+ return handleOptions(request);
+ }
+
+ // NOTE: If you are selfhosting this proxy, you can remove this check, or can set it to only accept requests from your own domain
+ if (!isAllowedOrigin(origin)) {
+ return new Response(JSON.stringify({
+ error: 'Forbidden',
+ message: 'This proxy only accepts requests from bentopdf.com',
+ }), {
+ status: 403,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+
+ if (request.method !== 'GET') {
+ return new Response('Method not allowed', {
+ status: 405,
+ headers: corsHeaders(origin),
+ });
+ }
+
+ const targetUrl = url.searchParams.get('url');
+ const timestamp = url.searchParams.get('t');
+ const signature = url.searchParams.get('sig');
+
+ if (env.PROXY_SECRET) {
+ if (!timestamp || !signature) {
+ return new Response(JSON.stringify({
+ error: 'Missing authentication parameters',
+ message: 'Request must include timestamp (t) and signature (sig) parameters',
+ }), {
+ status: 401,
+ headers: {
+ ...corsHeaders(origin),
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+
+ const requestTime = parseInt(timestamp, 10);
+ const now = Date.now();
+ if (isNaN(requestTime) || Math.abs(now - requestTime) > MAX_TIMESTAMP_AGE_MS) {
+ return new Response(JSON.stringify({
+ error: 'Request expired or invalid timestamp',
+ message: 'Timestamp must be within 5 minutes of current time',
+ }), {
+ status: 401,
+ headers: {
+ ...corsHeaders(origin),
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+
+ const message = `${targetUrl}${timestamp}`;
+ const isValid = await verifySignature(message, signature, env.PROXY_SECRET);
+
+ if (!isValid) {
+ return new Response(JSON.stringify({
+ error: 'Invalid signature',
+ message: 'Request signature verification failed',
+ }), {
+ status: 401,
+ headers: {
+ ...corsHeaders(origin),
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+ }
+
+ if (!targetUrl) {
+ return new Response(JSON.stringify({
+ error: 'Missing url parameter',
+ usage: 'GET /?url=
- © 2025 BentoPDF. All rights reserved.
+ © 2026 BentoPDF. All rights reserved.
Version
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 2dc72bf..1e6b4ab 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -9,3 +9,6 @@ services:
ports:
- '8080:8080'
restart: unless-stopped
+ # For IPv4-only environments
+ #environment:
+ # - DISABLE_IPV6=true
diff --git a/docker-compose.yml b/docker-compose.yml
index 494eff5..4999418 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,3 +7,6 @@ services:
restart: unless-stopped
ports:
- '8080:8080'
+ # For IPv4-only environments
+ #environment:
+ # - DISABLE_IPV6=true
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 47d72a3..391fca8 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -8,7 +8,7 @@ export default defineConfig({
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
- logo: '/images/favicon.svg',
+ logo: '/images/favicon-no-bg.svg',
nav: [
{ text: 'Home', link: '/' },
@@ -65,7 +65,7 @@ export default defineConfig({
footer: {
message: 'Dual-licensed under AGPL-3.0 and Commercial License.',
- copyright: 'Copyright © 2025 BentoPDF'
+ copyright: 'Copyright © 2026 BentoPDF'
},
search: {
diff --git a/docs/self-hosting/cloudflare.md b/docs/self-hosting/cloudflare.md
index 6904cef..0d8850f 100644
--- a/docs/self-hosting/cloudflare.md
+++ b/docs/self-hosting/cloudflare.md
@@ -76,3 +76,44 @@ npm run build
### Worker Size Limits
If using Cloudflare Workers for advanced routing, note the 1 MB limit for free plans.
+
+## CORS Proxy Worker (For Digital Signatures)
+
+The Digital Signature tool requires a CORS proxy to fetch certificate chains. Deploy the included worker:
+
+```bash
+cd cloudflare
+npx wrangler login
+npx wrangler deploy
+```
+
+### Security Features
+
+| Feature | Description |
+|---------|-------------|
+| **URL Restrictions** | Only certificate URLs allowed |
+| **File Size Limit** | Max 10MB per request |
+| **Rate Limiting** | 60 req/IP/min (requires KV) |
+| **Private IP Blocking** | Blocks localhost, internal IPs |
+
+### Enable Rate Limiting
+
+```bash
+# Create KV namespace
+npx wrangler kv namespace create "RATE_LIMIT_KV"
+
+# Add to wrangler.toml with returned ID:
+# [[kv_namespaces]]
+# binding = "RATE_LIMIT_KV"
+# id = "YOUR_ID"
+
+npx wrangler deploy
+```
+
+### Build with Proxy URL
+
+```bash
+VITE_CORS_PROXY_URL=https://your-worker.workers.dev npm run build
+```
+
+> **Note:** See [README](https://github.com/alam00000/bentopdf#digital-signature-cors-proxy-required) for HMAC signature setup.
diff --git a/docs/self-hosting/cors-proxy.md b/docs/self-hosting/cors-proxy.md
new file mode 100644
index 0000000..2797a19
--- /dev/null
+++ b/docs/self-hosting/cors-proxy.md
@@ -0,0 +1,116 @@
+# CORS Proxy for Certificate Fetching
+
+The digital signature tool uses a CORS proxy to fetch issuer certificates from external Certificate Authorities (CAs). This is necessary because many CA servers don't include CORS headers in their responses, which prevents direct browser-based fetching.
+
+## How It Works
+
+When signing a PDF with a certificate:
+
+1. The `zgapdfsigner` library tries to build a complete certificate chain
+2. It fetches issuer certificates from URLs embedded in your certificate's AIA (Authority Information Access) extension
+3. These requests are routed through a CORS proxy that adds the necessary `Access-Control-Allow-Origin` headers
+4. The proxy returns the certificate data to the browser
+
+## Self-Hosting the CORS Proxy
+
+If you're self-hosting BentoPDF, you'll need to deploy your own CORS proxy.
+
+### Option 1: Cloudflare Workers (Recommended)
+
+1. **Install Wrangler CLI**:
+ ```bash
+ npm install -g wrangler
+ ```
+
+2. **Login to Cloudflare**:
+ ```bash
+ wrangler login
+ ```
+
+3. **Deploy the proxy**:
+ ```bash
+ cd cloudflare
+ wrangler deploy
+ ```
+
+4. **Update your environment**:
+ Create a `.env` or set in your hosting platform:
+ ```
+ VITE_CORS_PROXY_URL=https://your-worker-name.your-subdomain.workers.dev
+ ```
+
+5. **Rebuild BentoPDF**:
+ ```bash
+ npm run build
+ ```
+
+### Option 2: Custom Backend Proxy
+
+You can also create your own proxy endpoint. The requirements are:
+
+1. Accept GET requests with a `url` query parameter
+2. Fetch the URL from your server (no CORS restrictions server-side)
+3. Return the response with these headers:
+ - `Access-Control-Allow-Origin: *` (or your specific origin)
+ - `Access-Control-Allow-Methods: GET, OPTIONS`
+ - `Content-Type: application/x-x509-ca-cert`
+
+Example Express.js implementation:
+
+```javascript
+app.get('/api/cert-proxy', async (req, res) => {
+ const targetUrl = req.query.url;
+
+ // Validate it's a certificate URL
+ if (!isValidCertUrl(targetUrl)) {
+ return res.status(400).json({ error: 'Invalid URL' });
+ }
+
+ try {
+ const response = await fetch(targetUrl);
+ const data = await response.arrayBuffer();
+
+ res.set('Access-Control-Allow-Origin', '*');
+ res.set('Content-Type', 'application/x-x509-ca-cert');
+ res.send(Buffer.from(data));
+ } catch (error) {
+ res.status(500).json({ error: 'Proxy error' });
+ }
+});
+```
+
+## Security Considerations
+
+The included Cloudflare Worker has several security measures:
+
+- **URL Validation**: Only allows certificate-related URLs (`.crt`, `.cer`, `.pem`, `/certs/`, `/ocsp`, `/crl`)
+- **Blocked Domains**: Prevents access to localhost and private IP ranges
+- **HTTP Methods**: Only allows GET requests
+
+## Disabling the Proxy
+
+If you don't want to use a CORS proxy, set the environment variable to an empty string:
+
+```
+VITE_CORS_PROXY_URL=
+```
+
+**Note**: Without the proxy, signing with certificates that require external chain fetching (like FNMT or some corporate CAs) will fail.
+
+## Troubleshooting
+
+### "Failed to fetch certificate chain" Error
+
+1. Check that your CORS proxy is deployed and accessible
+2. Verify the `VITE_CORS_PROXY_URL` is correctly set
+3. Test the proxy directly:
+ ```bash
+ curl "https://your-proxy.workers.dev?url=https://www.cert.fnmt.es/certs/ACUSU.crt"
+ ```
+
+### Certificates That Work Without Proxy
+
+Some certificates include the full chain in the P12/PFX file and don't require external fetching:
+- Self-signed certificates
+- Some commercial CAs that bundle intermediate certificates
+- Certificates you've manually assembled with the full chain
diff --git a/docs/self-hosting/docker.md b/docs/self-hosting/docker.md
index 1c33236..0130afd 100644
--- a/docs/self-hosting/docker.md
+++ b/docs/self-hosting/docker.md
@@ -2,6 +2,15 @@
The easiest way to self-host BentoPDF in a production environment.
+> [!IMPORTANT]
+> **Required Headers for Office File Conversion**
+>
+> LibreOffice-based tools (Word, Excel, PowerPoint conversion) require these HTTP headers for `SharedArrayBuffer` support:
+> - `Cross-Origin-Opener-Policy: same-origin`
+> - `Cross-Origin-Embedder-Policy: require-corp`
+>
+> The official Docker images include these headers. If using a reverse proxy (Traefik, Caddy, etc.), ensure these headers are preserved or added.
+
## Quick Start
```bash
@@ -106,6 +115,10 @@ services:
- "traefik.http.routers.bentopdf.entrypoints=websecure"
- "traefik.http.routers.bentopdf.tls.certresolver=letsencrypt"
- "traefik.http.services.bentopdf.loadbalancer.server.port=8080"
+ # Required headers for SharedArrayBuffer (LibreOffice WASM)
+ - "traefik.http.routers.bentopdf.middlewares=bentopdf-headers"
+ - "traefik.http.middlewares.bentopdf-headers.headers.customresponseheaders.Cross-Origin-Opener-Policy=same-origin"
+ - "traefik.http.middlewares.bentopdf-headers.headers.customresponseheaders.Cross-Origin-Embedder-Policy=require-corp"
restart: unless-stopped
```
@@ -135,6 +148,8 @@ Caddyfile:
```
pdf.example.com {
reverse_proxy bentopdf:8080
+ header Cross-Origin-Opener-Policy "same-origin"
+ header Cross-Origin-Embedder-Policy "require-corp"
}
```
diff --git a/docs/self-hosting/index.md b/docs/self-hosting/index.md
index f8de37c..a492ec2 100644
--- a/docs/self-hosting/index.md
+++ b/docs/self-hosting/index.md
@@ -81,6 +81,7 @@ Choose your platform:
- [Nginx](/self-hosting/nginx)
- [Apache](/self-hosting/apache)
- [Docker](/self-hosting/docker)
+- [CORS Proxy](/self-hosting/cors-proxy) - Required for digital signatures
## System Requirements
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..d1130f8
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,47 @@
+import globals from "globals";
+import pluginJs from "@eslint/js";
+import tseslint from "typescript-eslint";
+import eslintConfigPrettier from "eslint-config-prettier";
+
+export default [
+ { files: ["**/*.{js,mjs,cjs,ts}"] },
+ { ignores: ["dist/**", "coverage/**", "node_modules/**", "**/.vitepress/cache/**", "public/**/*.min.js"] },
+ { languageOptions: { globals: { ...globals.browser, ...globals.node } } },
+ pluginJs.configs.recommended,
+ ...tseslint.configs.recommended,
+ eslintConfigPrettier,
+ {
+ rules: {
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
+ "@typescript-eslint/no-unused-expressions": "warn",
+ "no-redeclare": "warn",
+ "no-constant-condition": "warn",
+ "no-ex-assign": "warn",
+ "no-empty": "warn",
+ "no-case-declarations": "warn",
+ "@typescript-eslint/no-this-alias": "warn",
+ "no-func-assign": "warn",
+ "no-fallthrough": "warn",
+ "no-cond-assign": "warn",
+ "no-irregular-whitespace": "warn",
+ "no-prototype-builtins": "warn",
+ "no-undef": "warn",
+ "no-useless-escape": "warn",
+ "no-unsafe-finally": "warn",
+ "no-self-assign": "warn",
+ "no-control-regex": "warn",
+ "@typescript-eslint/no-require-imports": "warn",
+ "getter-return": "warn",
+ "no-constant-binary-expression": "warn",
+ "@typescript-eslint/ban-ts-comment": "warn",
+ "no-unused-private-class-members": "warn",
+ "no-unreachable": "warn",
+ "no-setter-return": "warn",
+ "no-useless-catch": "warn",
+ "valid-typeof": "warn",
+ "no-sparse-arrays": "warn",
+ "no-misleading-character-class": "warn"
+ }
+ }
+];
diff --git a/faq.html b/faq.html
index 1991fbe..00f40c0 100644
--- a/faq.html
+++ b/faq.html
@@ -6,28 +6,31 @@
- © 2025 BentoPDF. All rights reserved.
+ © 2026 BentoPDF. All rights reserved.
Version
diff --git a/index.html b/index.html
index 7808b84..0770c6f 100644
--- a/index.html
+++ b/index.html
@@ -69,7 +69,7 @@
- © 2025 BentoPDF. All rights reserved.
+ © 2026 BentoPDF. All rights reserved.
Version
diff --git a/licensing.html b/licensing.html
index 8e9fe18..20b3f12 100644
--- a/licensing.html
+++ b/licensing.html
@@ -4,30 +4,33 @@
- © 2025 BentoPDF. All rights reserved.
+ © 2026 BentoPDF. All rights reserved.
Version
@@ -820,8 +823,8 @@
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
- Convert your PDFs to and from 40+ different file formats with BentoPDF's comprehensive conversion tools. Transform PDFs to Word, Excel, images, text, and many other formats - or convert files into PDFs. All conversions happen locally in your browser, ensuring complete privacy. No file uploads, no cloud processing, completely free.
+ Convert your PDFs to and from 40+ different file formats with BentoPDF's comprehensive conversion tools.
+ Transform PDFs to Word, Excel, images, text, and many other formats - or convert files into PDFs. All
+ conversions happen locally in your browser, ensuring complete privacy. No file uploads, no cloud
+ processing, completely free.
All operations happen in your browser. Your files never leave your device, never uploaded to servers. Complete privacy guaranteed. All operations happen in your browser. Your files never leave your device,
+ never uploaded to servers. Complete privacy guaranteed. Use all tools as many times as you want, with files of any size. No file limits, no tool limits. Completely free forever. Use all tools as many times as you want, with files of any size. No file
+ limits, no tool limits. Completely free forever. Browser-based processing means instant results. No waiting for uploads or downloads. Works offline after page load. Browser-based processing means instant results. No waiting for uploads or
+ downloads. Works offline after page load. Start using tools immediately. No account creation, no email, no personal information needed. Just open and use. Start using tools immediately. No account creation, no email, no personal
+ information needed. Just open and use. Yes! All BentoPDF pdf converter tools are 100% free with no hidden fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Yes! All BentoPDF pdf converter tools are 100% free with no hidden
+ fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Absolutely! All processing happens entirely in your browser. Your files never leave your device, are never uploaded to servers. Complete privacy guaranteed. Absolutely! All processing happens entirely in your browser. Your
+ files never leave your device, are never uploaded to servers. Complete privacy guaranteed. No file size limits! Process files of any size. Browser-based processing means no artificial limitations. No file size limits! Process files of any size. Browser-based
+ processing means no artificial limitations. No installation required! All tools are web-based and work directly in your browser. Just open the page and start using - works on any device. No installation required! All tools are web-based and work directly in
+ your browser. Just open the page and start using - works on any device. © 2025 BentoPDF. All rights reserved. © 2026 BentoPDF. All rights reserved. Version
- Edit and enhance your PDF files with BentoPDF's comprehensive suite of 25+ editing tools. Compress file sizes, rotate pages, crop documents, add watermarks, insert page numbers, change colors, and much more. All editing happens in your browser - your files stay completely private. No signup, no limits, completely free.
+ Edit and enhance your PDF files with BentoPDF's comprehensive suite of 25+ editing tools. Compress file
+ sizes, rotate pages, crop documents, add watermarks, insert page numbers, change colors, and much more.
+ All editing happens in your browser - your files stay completely private. No signup, no limits,
+ completely free.
All operations happen in your browser. Your files never leave your device, never uploaded to servers. Complete privacy guaranteed. All operations happen in your browser. Your files never leave your device,
+ never uploaded to servers. Complete privacy guaranteed. Use all tools as many times as you want, with files of any size. No file limits, no tool limits. Completely free forever. Use all tools as many times as you want, with files of any size. No file
+ limits, no tool limits. Completely free forever. Browser-based processing means instant results. No waiting for uploads or downloads. Works offline after page load. Browser-based processing means instant results. No waiting for uploads or
+ downloads. Works offline after page load. Start using tools immediately. No account creation, no email, no personal information needed. Just open and use. Start using tools immediately. No account creation, no email, no personal
+ information needed. Just open and use. Yes! All BentoPDF pdf editor tools are 100% free with no hidden fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Yes! All BentoPDF pdf editor tools are 100% free with no hidden fees,
+ no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Absolutely! All processing happens entirely in your browser. Your files never leave your device, are never uploaded to servers. Complete privacy guaranteed. Absolutely! All processing happens entirely in your browser. Your
+ files never leave your device, are never uploaded to servers. Complete privacy guaranteed. No file size limits! Process files of any size. Browser-based processing means no artificial limitations. No file size limits! Process files of any size. Browser-based
+ processing means no artificial limitations. No installation required! All tools are web-based and work directly in your browser. Just open the page and start using - works on any device. No installation required! All tools are web-based and work directly in
+ your browser. Just open the page and start using - works on any device. © 2025 BentoPDF. All rights reserved. © 2026 BentoPDF. All rights reserved. Version
- Combine multiple PDF files into one document or split large PDFs into smaller files with BentoPDF's merge and split tools. Reorder pages, extract specific sections, alternate merge documents, and organize your PDFs exactly how you need them. Browser-based processing ensures your files stay private. No limits on file count or size.
+ Combine multiple PDF files into one document or split large PDFs into smaller files with BentoPDF's
+ merge and split tools. Reorder pages, extract specific sections, alternate merge documents, and organize
+ your PDFs exactly how you need them. Browser-based processing ensures your files stay private. No limits
+ on file count or size.
All operations happen in your browser. Your files never leave your device, never uploaded to servers. Complete privacy guaranteed. All operations happen in your browser. Your files never leave your device,
+ never uploaded to servers. Complete privacy guaranteed. Use all tools as many times as you want, with files of any size. No file limits, no tool limits. Completely free forever. Use all tools as many times as you want, with files of any size. No file
+ limits, no tool limits. Completely free forever. Browser-based processing means instant results. No waiting for uploads or downloads. Works offline after page load. Browser-based processing means instant results. No waiting for uploads or
+ downloads. Works offline after page load. Start using tools immediately. No account creation, no email, no personal information needed. Just open and use. Start using tools immediately. No account creation, no email, no personal
+ information needed. Just open and use. Yes! All BentoPDF pdf merge & split tools are 100% free with no hidden fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Yes! All BentoPDF pdf merge & split tools are 100% free with no hidden
+ fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Absolutely! All processing happens entirely in your browser. Your files never leave your device, are never uploaded to servers. Complete privacy guaranteed. Absolutely! All processing happens entirely in your browser. Your
+ files never leave your device, are never uploaded to servers. Complete privacy guaranteed. No file size limits! Process files of any size. Browser-based processing means no artificial limitations. No file size limits! Process files of any size. Browser-based
+ processing means no artificial limitations. No installation required! All tools are web-based and work directly in your browser. Just open the page and start using - works on any device. No installation required! All tools are web-based and work directly in
+ your browser. Just open the page and start using - works on any device. © 2025 BentoPDF. All rights reserved. © 2026 BentoPDF. All rights reserved. Version
- Protect and secure your PDF documents with BentoPDF's comprehensive security tools. Encrypt with passwords, add digital signatures, manage permissions, remove sensitive data, and more. All security operations happen locally in your browser for maximum privacy. No cloud uploads, no data retention - your documents stay completely confidential.
+ Protect and secure your PDF documents with BentoPDF's comprehensive security tools. Encrypt with
+ passwords, add digital signatures, manage permissions, remove sensitive data, and more. All security
+ operations happen locally in your browser for maximum privacy. No cloud uploads, no data retention -
+ your documents stay completely confidential.
All operations happen in your browser. Your files never leave your device, never uploaded to servers. Complete privacy guaranteed. All operations happen in your browser. Your files never leave your device,
+ never uploaded to servers. Complete privacy guaranteed. Use all tools as many times as you want, with files of any size. No file limits, no tool limits. Completely free forever. Use all tools as many times as you want, with files of any size. No file
+ limits, no tool limits. Completely free forever. Browser-based processing means instant results. No waiting for uploads or downloads. Works offline after page load. Browser-based processing means instant results. No waiting for uploads or
+ downloads. Works offline after page load. Start using tools immediately. No account creation, no email, no personal information needed. Just open and use. Start using tools immediately. No account creation, no email, no personal
+ information needed. Just open and use. Yes! All BentoPDF pdf security tools are 100% free with no hidden fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Yes! All BentoPDF pdf security tools are 100% free with no hidden
+ fees, no premium tiers, and no subscription required. Use unlimited tools, unlimited times. Absolutely! All processing happens entirely in your browser. Your files never leave your device, are never uploaded to servers. Complete privacy guaranteed. Absolutely! All processing happens entirely in your browser. Your
+ files never leave your device, are never uploaded to servers. Complete privacy guaranteed. No file size limits! Process files of any size. Browser-based processing means no artificial limitations. No file size limits! Process files of any size. Browser-based
+ processing means no artificial limitations. No installation required! All tools are web-based and work directly in your browser. Just open the page and start using - works on any device. No installation required! All tools are web-based and work directly in
+ your browser. Just open the page and start using - works on any device. © 2025 BentoPDF. All rights reserved. © 2026 BentoPDF. All rights reserved. Version
+
BentoPDF
@@ -125,11 +128,11 @@
+
BentoPDF
+
BentoPDF
@@ -267,11 +270,11 @@
+
BentoPDF
+
BentoPDF
@@ -732,11 +735,11 @@
+
BentoPDF
PDF Converter - Free Online PDF Conversion Tools
+ PDF Converter - Free Online PDF Conversion Tools
+
All PDF Converter Tools
-
+
Why Use BentoPDF PDF Converter Tools?
-
+ Why Use BentoPDF PDF Converter Tools?
+
+
100% Privacy-First
- Unlimited & Free
- Lightning Fast
- No Signup Required
- Frequently Asked Questions
-
+
Are these pdf converter tools really free?
-
Are my files private and secure?
-
Is there a file size limit?
-
Do I need to install software?
-
+
BentoPDF
PDF Editor - Free Online PDF Editing Tools
All PDF Editor Tools
-
+
Why Use BentoPDF PDF Editor Tools?
-
+ Why Use BentoPDF PDF Editor Tools?
+
+
100% Privacy-First
- Unlimited & Free
- Lightning Fast
- No Signup Required
- Frequently Asked Questions
-
+
Are these pdf editor tools really free?
-
Are my files private and secure?
-
Is there a file size limit?
-
Do I need to install software?
-
+
BentoPDF
PDF Merge & Split Tools - Combine & Separate PDFs Free
+ PDF Merge & Split Tools - Combine & Separate PDFs
+ Free
All PDF Merge & Split Tools
-
+
Why Use BentoPDF PDF Merge & Split Tools?
-
+ Why Use BentoPDF PDF Merge & Split
+ Tools?
+
100% Privacy-First
- Unlimited & Free
- Lightning Fast
- No Signup Required
- Frequently Asked Questions
-
+
Are these pdf merge & split tools really free?
-
Are my files private and secure?
-
Is there a file size limit?
-
Do I need to install software?
-
+
BentoPDF
PDF Security Tools - Protect & Secure Your PDFs Free
+ PDF Security Tools - Protect & Secure Your PDFs
+ Free
All PDF Security Tools
-
+
Why Use BentoPDF PDF Security Tools?
-
+ Why Use BentoPDF PDF Security Tools?
+
+
100% Privacy-First
- Unlimited & Free
- Lightning Fast
- No Signup Required
- Frequently Asked Questions
-
+
Are these pdf security tools really free?
-
Are my files private and secure?
-
Is there a file size limit?
-
Do I need to install software?
-
+
BentoPDF
Company
+
+
+ Legal
+
+
+ Company
+
+
+ Legal
+
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+ Company
+
+
+ Legal
+
+