Fix a Laravel/Vue html2canvas export by first finding the server that delivers the missing image. If that server is yours, return an Access-Control-Allow-Origin header for the requesting page and set useCORS: true. If it is a CDN or third-party host, Laravel cannot change its policy; configure that host or fetch the image through a tightly restricted same-origin proxy. A canvas that still fails after CORS is correct may be hitting browser canvas limits or unsupported CSS rather than a CORS problem.
What the error actually means
html2canvas does not capture the browser’s pixels like an operating-system screenshot. It walks the DOM and recreates supported styles, images and elements on a new canvas. Consequently, the result can differ from what is visible on screen.
When an image comes from another origin without CORS approval, drawing it onto a canvas taints that canvas. Browser security then blocks pixel-reading operations such as toBlob() and toDataURL(), usually with a SecurityError. html2canvas defaults allowTaint to false, so it normally skips unsafe images instead of bypassing browser policy.
1. Find the server causing the failure
- Open DevTools and reproduce the export.
- In Console and Network, locate every failed or missing image request. Record the final URL after redirects, status code, and response headers.
- Compare the image origin (scheme, host and port) with the origin of the Vue page. A Laravel API and a separate image CDN are different origins even when they share a company domain.
- Check whether the response contains
Access-Control-Allow-Originmatching the page origin. A wildcard is not suitable for credentialed requests.
Do not start by changing Laravel configuration unless the failing URL is actually served by Laravel. The browser evaluates the headers on the image response, not on an unrelated API response.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errors#1 Best Overall
2. Direct CORS when you control the image host
Return an appropriate response header
Configure the server that serves the image to return Access-Control-Allow-Origin: https://your-vue.example (use your real production origin). If requests include cookies or authorization, also return Access-Control-Allow-Credentials: true and a specific origin, never *. Add Vary: Origin when responses vary by origin and ensure redirects preserve the policy.
Laravel 12.x configuration
Laravel 12.x automatically handles CORS OPTIONS requests through the globally included HandleCors middleware. To customize allowed origins, methods and headers, publish the configuration file:
php artisan config:publish cors
Edit the published config/cors.php for the routes Laravel serves. Typical values include your Vue origin in allowed_origins, the required HTTP methods in allowed_methods, and headers such as Content-Type and Authorization. Clear cached configuration after changes with php artisan config:clear (or rebuild your deployment cache). This only affects responses generated by this Laravel application; it cannot add headers to a third-party CDN.
3. Enable CORS loading in Vue
Use a component ref and set useCORS: true. This tells html2canvas to request images in CORS mode; it does not grant permission on the remote server.
import { ref } from 'vue'
import html2canvas from 'html2canvas'
const exportArea = ref(null)
async function exportImage() {
const canvas = await html2canvas(exportArea.value, {
useCORS: true,
onError: (error) => console.warn('html2canvas resource failed:', error.message)
})
const blob = await new Promise((resolve) =>
canvas.toBlob(resolve, 'image/png')
)
if (!blob) throw new Error('Canvas export returned no image data')
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = 'export.png'
link.click()
URL.revokeObjectURL(url)
}
Use await nextTick() before capture if Vue has just rendered data, and wait for images to finish loading when the page inserts them asynchronously. A failed image may be omitted while the rest of the export succeeds; inspect the warning and Network panel rather than assuming the entire canvas is broken.
4. Use a same-origin proxy when the image host cannot change
html2canvas supports a proxy option that loads a remote image through your page’s origin. Your proxy fetches the destination server-side, validates the response, and returns the bytes with an appropriate content type. Keep it a security boundary:
- Allow-list destination hosts and, where possible, path prefixes.
- Accept only
httpsURLs and reject private, loopback and link-local IP ranges to prevent SSRF. - Limit response size, MIME types, redirects, timeout and concurrency.
- Do not forward arbitrary incoming headers or credentials.
- Cache approved assets and log the destination and result for abuse investigation.
Point html2canvas’s proxy option at this endpoint. A proxy is a fallback, not a way to expose an unrestricted URL-fetch service.
5. Distinguish CORS from other export failures
Images are missing but export downloads
The image request was blocked, redirected without CORS, returned an unexpected content type, or loaded after capture began. Fix the image response, use a controlled proxy, or wait for the image before calling html2canvas.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →toBlob() or toDataURL() throws SecurityError
Some cross-origin content was drawn without CORS approval and tainted the canvas. Find every image, including CSS background images and images inside components, and correct each response. Setting allowTaint: true does not make export safe; it permits drawing while leaving the canvas unreadable.
The canvas is blank or only partly rendered
Very large dimensions can exceed browser canvas width, height or area limits, which vary by browser and platform. Capture a smaller region, reduce scale, split a long document into pages, or use PDF/server-side capture. Unsupported CSS can also produce visual differences because html2canvas implements only selected CSS properties.
The console shows a preflight failure
For requests that trigger preflight, the image server must answer OPTIONS with allowed origin, method and requested headers. Verify that the preflight is reaching the intended host and that redirects are not sending it elsewhere.
Choosing the right remedy
| Situation | Best path | Reason |
|---|---|---|
| You control the image server | Direct CORS headers plus useCORS: true |
Fewest moving parts and no proxy bandwidth. |
| Image is on a vendor CDN | Ask the vendor for CORS support or use a restricted proxy | Laravel cannot alter another origin’s response. |
| Images are approved but export is huge | Reduce dimensions/scale or split captures | Canvas limits are separate from CORS. |
| You need a server-generated screenshot | Use a screenshot API | A browser service can capture without shipping DOM reconstruction to the client. |
Troubleshooting checklist
- Wrong origin: follow redirects and configure the final image host.
- Header mismatch: make the value exactly match the Vue origin, including scheme and port.
- Credentials: use a specific origin with
Access-Control-Allow-Credentials: true. - Cached old headers: purge CDN caches and verify the response in a private window.
- Lazy images: scroll or otherwise trigger loading, then await
img.decode()where supported. - Proxy errors: inspect allow-list, DNS/IP validation, timeout and response-size logs.
- CSS mismatch: simplify unsupported filters, blend modes, pseudo-elements or complex fonts.
- Blank export: capture a smaller element to determine whether dimensions are the cause.
Performance, reliability and cost considerations
Client-side html2canvas consumes the user’s CPU and memory. Full-page, high-retina captures increase work roughly with pixel area; capture only the required element, choose a sensible scale, and avoid repeatedly exporting in a reactive render loop. For long pages, split by logical sections and combine the resulting files.
Rank #4
Direct CORS avoids proxy transfer costs. A proxy adds server bandwidth, latency and an attack surface, so enforce the limits above. If a capture is business-critical, report failures to the user and retain the original URL and browser error for diagnosis rather than silently producing an incomplete image.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo is the first alternative to try when you need a clean server-side website screenshot: cookie banners, newsletter popups and chat widgets are removed before capture, and only clean shots are billed. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed; each response identifies the result with X-Page-Verdict and X-Billed headers. It also provides an MCP server for Claude, Cursor and other MCP clients, with take_screenshot, get_page_info and capture_pdf tools.
One GET request returns PNG, JPEG, WebP or PDF:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the complete option list and request details in the ScreenshotNeo documentation. Features include full-page lazy-image capture, CSS-selector elements, device presets, custom viewport and retina scale, PDF page controls, custom CSS/JavaScript, clicks, waits, blocked resources, headers, cookies, authorization, geolocation, caching, signed links, asynchronous webhooks, bulk capture and a usage API.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; all features are included on every plan. Create a free ScreenshotNeo account.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →FAQ
Can I fix a third-party image by adding CORS in Laravel?
No. The response must come from the server that supplied the image, or from a proxy you operate.
Does useCORS make an image public?
No. It only asks the browser to use CORS when fetching; the remote response still has to grant the requesting origin.
Why does the page look correct while the export is wrong?
Normal display can show an image without allowing canvas pixel reads. html2canvas additionally reconstructs supported DOM and CSS, so CORS, canvas limits and unsupported styles can each change the result.
Frequently Asked Questions
Can I fix a third-party image by adding CORS in Laravel?
No. The response must come from the server that supplied the image, or from a proxy you operate.
Does useCORS make an image public?
No. It only asks the browser to use CORS; the remote response still has to grant the requesting origin.
Why does the page look correct while the export is wrong?
Browser display, canvas security and html2canvas’s DOM/CSS reconstruction have different rules, so a visible page can still produce a missing or incomplete export.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

