feat: Enterprise & Server evaluation ISOs via Microsoft Eval Center #13

Merged
starkSV merged 7 commits from feat/evalcenter-isos into main 2026-05-18 22:21:20 +05:30
17 changed files with 917 additions and 21 deletions

View file

@ -14,10 +14,11 @@
MSDL replicates the session flow that Microsoft uses to serve ISO download links — identical to the approach used by [Rufus/Fido](https://github.com/pbatard/Fido). You get the exact same signed CDN URL Microsoft would give you, just without the browser requirement.
- ✅ Direct Microsoft CDN links — no proxying of actual file data
- ✅ 38 languages per release
- ✅ 38 languages per consumer release
- ✅ ARM64 + x64 + x86 support
- ✅ Windows Server 20162025 and Windows 11 Enterprise evaluation ISOs
- ✅ No account, no browser lock, no ads, no tracking
- ✅ Links expire in 24 hours (Microsoft's standard behaviour, not a limitation)
- ✅ Consumer links expire in 24 hours (Microsoft's standard behaviour, not a limitation)
---
@ -133,10 +134,27 @@ Returns signed download links from Microsoft's CDN.
}
```
### `GET /evallinks?product=<slug>`
Returns direct CDN links for evaluation ISOs (Server/Enterprise). Links are resolved from Microsoft's Eval Center fwlink redirects and cached for 24 hours.
Valid slugs: `server-2025`, `server-2022`, `server-2019`, `server-2016`, `win11-ent`
```json
{
"links": [
{ "arch": "x64", "lang": "en-us", "url": "https://software-static.download.prss.microsoft.com/..." },
{ "arch": "x64", "lang": "fr-fr", "url": "https://software-static.download.prss.microsoft.com/..." }
]
}
```
---
## Supported Products
### Consumer releases
| Product | ID | Architecture |
|---|---|---|
| Windows 11 25H2 | 3262 | x64 |
@ -150,6 +168,18 @@ Returns signed download links from Microsoft's CDN.
| Windows 8.1 | 52 | x64 / x86 |
| Windows 8.1 Single Language | 48 | x64 / x86 |
### Evaluation editions (Server & Enterprise)
180-day trial ISOs sourced directly from Microsoft's Eval Center CDN. No registration required.
| Product | Slug | Architecture |
|---|---|---|
| Windows Server 2025 | `server-2025` | x64 |
| Windows Server 2022 | `server-2022` | x64 |
| Windows Server 2019 | `server-2019` | x64 |
| Windows Server 2016 | `server-2016` | x64 |
| Windows 11 Enterprise | `win11-ent` | x64 |
---
## Tech Stack
@ -195,12 +225,18 @@ Recommended setup:
## Contributing
Pull requests welcome. To add a new Windows release:
Pull requests welcome. To add a new consumer Windows release:
1. Find the product ID on `www.microsoft.com/software-download-connector/api/`
2. Add it to `frontend/public/data/products.json`
3. Add related metadata in `ProductDetailPage.tsx` (`PRODUCT_META`, `RELATED_GROUPS`)
4. Update the product table in this README
2. Add it to `frontend/public/data/products.json` (name, archs, badge, related, active)
3. Update the product table in this README
To add a new evaluation edition:
1. Find the fwlink URL on `microsoft.com/en-us/evalcenter/download-*`
2. Add the slug and fwlink to the `evalProducts` map in `backend/main.go`
3. Add the product config to `frontend/src/data/evalProducts.ts`
4. Update the eval table in this README
---

View file

@ -4,13 +4,16 @@ import (
"bytes"
"encoding/json"
"fmt"
"html"
"io"
"log"
"net/http"
"net/url"
"os"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"time"
@ -39,6 +42,146 @@ var (
validSkuID = regexp.MustCompile(`^[a-zA-Z0-9_\-]+$`)
)
// --- Evalcenter (Enterprise / Server eval ISOs) ---
type EvalProduct struct {
Name string
EvalURL string
}
type EvalLink struct {
Arch string `json:"arch"`
Lang string `json:"lang"`
URL string `json:"url"`
}
type EvalLinksResponse struct {
Product string `json:"product"`
Name string `json:"name"`
Links []EvalLink `json:"links"`
}
type evalCacheEntry struct {
Links []EvalLink
CachedAt time.Time
}
var (
evalProductMap = map[string]EvalProduct{
"server-2025": {Name: "Windows Server 2025", EvalURL: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2025"},
"server-2022": {Name: "Windows Server 2022", EvalURL: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2022"},
"server-2019": {Name: "Windows Server 2019", EvalURL: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2019"},
"server-2016": {Name: "Windows Server 2016", EvalURL: "https://www.microsoft.com/en-us/evalcenter/download-windows-server-2016"},
"win11-ent": {Name: "Windows 11 Enterprise", EvalURL: "https://www.microsoft.com/en-us/evalcenter/download-windows-11-enterprise"},
}
evalCache = make(map[string]evalCacheEntry)
evalCacheMu sync.RWMutex
evalCacheTTL = 24 * time.Hour
fwlinkRe = regexp.MustCompile(`https://go\.microsoft\.com/fwlink/[^"'\s<>]+`)
isoLangRe = regexp.MustCompile(`_([a-z]{2}-[a-z]{2})\.iso$`)
)
func detectArch(rawURL string) string {
lower := strings.ToLower(rawURL)
if strings.Contains(lower, "arm64") {
return "ARM64"
}
if strings.Contains(lower, "x64") {
return "x64"
}
if strings.Contains(lower, "x86") {
return "x86"
}
return "ISO"
}
func detectLang(rawURL string) string {
m := isoLangRe.FindStringSubmatch(strings.ToLower(rawURL))
if len(m) > 1 {
return m[1]
}
return ""
}
func fetchEvalLinks(evalURL string) ([]EvalLink, error) {
client := &http.Client{Timeout: 15 * time.Second}
// Step 1: Fetch the evalcenter page to extract fwlinks
req, _ := http.NewRequest("GET", evalURL, nil)
req.Header.Set("User-Agent", UA)
req.Header.Set("Accept", "text/html,application/xhtml+xml")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("fetching evalcenter page: %w", err)
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
// Step 2: Extract all fwlink URLs (unescape HTML entities like &amp; → &)
rawMatches := fwlinkRe.FindAllString(string(bodyBytes), -1)
// Deduplicate fwlinks
seen := map[string]bool{}
var fwlinks []string
for _, raw := range rawMatches {
fwlink := html.UnescapeString(raw)
if !seen[fwlink] {
seen[fwlink] = true
fwlinks = append(fwlinks, fwlink)
}
}
// Step 3: Follow all redirects in parallel
type result struct {
link EvalLink
ok bool
}
results := make([]result, len(fwlinks))
var wg sync.WaitGroup
for i, fwlink := range fwlinks {
wg.Add(1)
go func(idx int, fw string) {
defer wg.Done()
fwReq, _ := http.NewRequest("GET", fw, nil)
fwReq.Header.Set("User-Agent", UA)
fwResp, err := client.Do(fwReq)
if err != nil {
return
}
fwResp.Body.Close()
finalURL := fwResp.Request.URL.String()
if !strings.Contains(strings.ToLower(finalURL), ".iso") {
return
}
results[idx] = result{
link: EvalLink{Arch: detectArch(finalURL), Lang: detectLang(finalURL), URL: finalURL},
ok: true,
}
}(i, fwlink)
}
wg.Wait()
var links []EvalLink
for _, r := range results {
if r.ok {
links = append(links, r.link)
}
}
// Sort: en-us first, then alphabetically by lang
sort.Slice(links, func(i, j int) bool {
if links[i].Lang == "en-us" {
return true
}
if links[j].Lang == "en-us" {
return false
}
return links[i].Lang < links[j].Lang
})
return links, nil
}
func setWorkerSecret(req *http.Request) {
if workerSecret != "" {
req.Header.Set("X-Worker-Secret", workerSecret)
@ -178,7 +321,7 @@ func enableCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// Allowed origins
if origin == "https://msdl.tech-latest.com" || origin == "http://localhost:5173" || origin == "http://localhost:3000" {
if origin == "https://msdl.tech-latest.com" || strings.HasPrefix(origin, "http://localhost:") {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
@ -440,14 +583,77 @@ func handleProxy(w http.ResponseWriter, r *http.Request) {
w.Write(buf.Bytes())
}
// --- /evallinks endpoint ---
func handleEvalLinks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
product := r.URL.Query().Get("product")
if product == "" {
respondJSONError(w, http.StatusBadRequest, "product query parameter is required")
return
}
evalProduct, ok := evalProductMap[product]
if !ok {
respondJSONError(w, http.StatusNotFound, "Unknown eval product: "+product)
return
}
// Check cache
evalCacheMu.RLock()
cached, exists := evalCache[product]
evalCacheMu.RUnlock()
if exists && time.Since(cached.CachedAt) < evalCacheTTL {
log.Printf("/evallinks: product=%s -> cache hit (%d links)\n", product, len(cached.Links))
json.NewEncoder(w).Encode(EvalLinksResponse{Product: product, Name: evalProduct.Name, Links: cached.Links})
return
}
links, err := fetchEvalLinks(evalProduct.EvalURL)
if err != nil {
respondJSONError(w, http.StatusBadGateway, "Failed to fetch eval links: "+err.Error())
return
}
if len(links) == 0 {
respondJSONError(w, http.StatusNotFound, "No download links found for this eval product")
return
}
evalCacheMu.Lock()
evalCache[product] = evalCacheEntry{Links: links, CachedAt: time.Now()}
evalCacheMu.Unlock()
log.Printf("/evallinks: product=%s -> %d links\n", product, len(links))
json.NewEncoder(w).Encode(EvalLinksResponse{Product: product, Name: evalProduct.Name, Links: links})
}
// warmEvalCache pre-fetches all eval product links in the background at startup.
func warmEvalCache() {
for slug, product := range evalProductMap {
go func(s string, p EvalProduct) {
links, err := fetchEvalLinks(p.EvalURL)
if err != nil || len(links) == 0 {
log.Printf("eval warm: %s failed: %v\n", s, err)
return
}
evalCacheMu.Lock()
evalCache[s] = evalCacheEntry{Links: links, CachedAt: time.Now()}
evalCacheMu.Unlock()
log.Printf("eval warm: %s -> %d links cached\n", s, len(links))
}(slug, product)
}
}
func main() {
go cleanupSessions()
go warmEvalCache()
mux := http.NewServeMux()
// API Routing
mux.HandleFunc("/skuinfo", handleSkuInfo)
mux.HandleFunc("/proxy", handleProxy)
mux.HandleFunc("/evallinks", handleEvalLinks)
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok"}`))

View file

@ -32,6 +32,19 @@
<changefreq>yearly</changefreq>
</url>
<!-- Enterprise & Server -->
<url>
<loc>https://msdl.tech-latest.com/eval</loc>
<lastmod>2026-05-18</lastmod>
<priority>0.8</priority>
<changefreq>monthly</changefreq>
</url>
<url><loc>https://msdl.tech-latest.com/product/server-2025</loc><lastmod>2026-05-18</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/server-2022</loc><lastmod>2026-05-18</lastmod><priority>0.9</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/server-2019</loc><lastmod>2026-05-18</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/server-2016</loc><lastmod>2026-05-18</lastmod><priority>0.7</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/win11-ent</loc><lastmod>2026-05-18</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<!-- Product Pages -->
<url><loc>https://msdl.tech-latest.com/product/48</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/52</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>

View file

@ -8,7 +8,17 @@ import ProductDetailPage from './pages/ProductDetailPage'
import AboutPage from './pages/AboutPage'
import PrivacyPolicyPage from './pages/PrivacyPolicyPage'
import DisclaimerPage from './pages/DisclaimerPage'
import EvalPage from './pages/EvalPage'
import EvalDetailPage from './pages/EvalDetailPage'
import NotFoundPage from './pages/NotFoundPage'
import { evalSlugSet } from './data/evalProducts'
import { useParams } from 'react-router-dom'
function ProductRouter() {
const { productId } = useParams<{ productId: string }>()
if (productId && evalSlugSet.has(productId)) return <EvalDetailPage />
return <ProductDetailPage />
}
import ScrollToTop from './components/ScrollToTop'
export default function App() {
@ -19,7 +29,8 @@ export default function App() {
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/products" element={<ProductsPage />} />
<Route path="/product/:productId" element={<ProductDetailPage />} />
<Route path="/product/:productId" element={<ProductRouter />} />
<Route path="/eval" element={<EvalPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/privacy-policy" element={<PrivacyPolicyPage />} />
<Route path="/disclaimer" element={<DisclaimerPage />} />

View file

@ -11,11 +11,12 @@ interface Row {
}
const rows: Row[] = [
{ feature: 'Direct CDN link', msdl: true, official: true, others: false },
{ feature: 'No browser required', msdl: true, official: false, others: 'varies' },
{ feature: 'ARM64 support', msdl: true, official: false, others: false },
{ feature: 'No account needed', msdl: true, official: true, others: 'varies' },
{ feature: 'No ads or tracking', msdl: true, official: true, others: false },
{ feature: 'Direct CDN link', msdl: true, official: true, others: false },
{ feature: 'No browser required', msdl: true, official: false, others: 'varies' },
{ feature: 'ARM64 support', msdl: true, official: false, others: false },
{ feature: 'Server & Enterprise ISOs', msdl: true, official: false, others: false },
{ feature: 'No account needed', msdl: true, official: true, others: 'varies' },
{ feature: 'No ads or tracking', msdl: true, official: true, others: false },
]
function Cell({ value, highlight = false }: { value: CellValue; highlight?: boolean }) {

View file

@ -24,7 +24,12 @@ export default function Dock() {
const [activeIndex, setActiveIndex] = useState(0)
useEffect(() => {
const idx = items.findIndex(item => item.path && item.path === location.pathname)
// Exact match first
let idx = items.findIndex(item => item.path && item.path === location.pathname)
// /product/* and /eval* → highlight Products tab
if (idx === -1 && (location.pathname.startsWith('/product/') || location.pathname.startsWith('/eval'))) {
idx = items.findIndex(item => item.path === '/products')
}
if (idx !== -1) setActiveIndex(idx)
}, [location.pathname])

View file

@ -24,6 +24,10 @@ const faqs: FaqItem[] = [
q: 'Can I use this with IDM or aria2?',
a: 'Absolutely — and we strongly recommend it. Microsoft throttles single-threaded browser downloads. Tools like aria2 (aria2c -x 16 -s 16 --disable-ipv6=true "URL") or IDM use 16 parallel connections, which typically give you full line speed on their CDN.',
},
{
q: 'Do you have Windows Server or Enterprise ISOs?',
a: 'Yes — check the Enterprise & Server section (/eval). It includes Windows Server 2025, 2022, 2019, 2016, and Windows 11 Enterprise. These are official 180-day evaluation editions sourced directly from Microsoft\'s Eval Center CDN — no browser session, no registration required. They\'re fully functional for testing and lab use; a valid license is needed to activate beyond the trial period.',
},
{
q: 'What\'s the difference between the various product IDs?',
a: 'Each Windows release has a unique product ID on Microsoft\'s servers. For example, 3262 is Windows 11 25H2 (x64) and 3265 is Windows 11 25H2 (ARM64). MSDL exposes all known IDs so you can always find the exact build you need.',

View file

@ -6,7 +6,7 @@ const steps = [
num: '1',
icon: LayoutGrid,
title: 'Pick a release',
desc: 'Choose from Windows 11, 10, and 8.1 across all feature updates.',
desc: 'Choose from Windows 11, 10, and 8.1 — or Server 20162025 and Enterprise evaluation editions.',
},
{
num: '2',

View file

@ -9,6 +9,8 @@ export default function SiteFooter() {
All ISO files are hosted on Microsoft's official CDN.
</p>
<div className="flex items-center justify-center gap-4 mt-2 text-[11px] text-zinc-700">
<Link to="/eval" className="hover:text-zinc-500 transition-colors">Enterprise & Server</Link>
<span>·</span>
<Link to="/privacy-policy" className="hover:text-zinc-500 transition-colors">Privacy Policy</Link>
<span>·</span>
<Link to="/disclaimer" className="hover:text-zinc-500 transition-colors">Disclaimer</Link>

View file

@ -0,0 +1,102 @@
export interface EvalProductConfig {
slug: string
name: string
version: string
build: string
description: string
seoDesc: string // ≤155 chars, for <meta name="description"> and og:description
archs: string[]
type: 'server' | 'enterprise'
requirements: {
cpu: string
ram: string
disk: string
note?: string
}
}
export const evalProducts: EvalProductConfig[] = [
{
slug: 'server-2025',
name: 'Windows Server 2025',
version: 'Standard / Datacenter',
build: 'Latest evaluation build',
seoDesc: 'Download Windows Server 2025 evaluation ISO from Microsoft. 180-day trial. Direct CDN link — no registration required.',
description:
'The latest Windows Server release featuring enhanced security, hybrid cloud capabilities, and improved performance. Includes SMB over QUIC, credential guard improvements, and delegated managed service accounts.',
archs: ['x64'],
type: 'server',
requirements: {
cpu: '1.4 GHz 64-bit',
ram: '512 MB / 2 GB GUI',
disk: '32 GB',
},
},
{
slug: 'server-2022',
name: 'Windows Server 2022',
version: 'Standard / Datacenter',
build: 'Latest evaluation build',
seoDesc: 'Download Windows Server 2022 evaluation ISO from Microsoft. Long-term servicing channel. 180-day trial. Direct CDN link, no registration.',
description:
'Long-term servicing channel release with Secured-core server support, TLS 1.3 by default, and DNS-over-HTTPS. The recommended choice for new server infrastructure evaluation.',
archs: ['x64'],
type: 'server',
requirements: {
cpu: '1.4 GHz 64-bit',
ram: '512 MB / 2 GB GUI',
disk: '32 GB',
},
},
{
slug: 'server-2019',
name: 'Windows Server 2019',
version: 'Standard / Datacenter',
build: '17763',
seoDesc: 'Download Windows Server 2019 evaluation ISO from Microsoft. Includes Defender ATP and Storage Migration Service. 180-day trial, direct CDN link.',
description:
'Stable and widely deployed. Includes Windows Defender Advanced Threat Protection, Storage Migration Service, and System Insights. Ideal for existing infrastructure evaluation.',
archs: ['x64'],
type: 'server',
requirements: {
cpu: '1.4 GHz 64-bit',
ram: '512 MB / 2 GB GUI',
disk: '32 GB',
},
},
{
slug: 'server-2016',
name: 'Windows Server 2016',
version: 'Standard / Datacenter',
build: '14393',
seoDesc: 'Download Windows Server 2016 evaluation ISO from Microsoft. Supports Nano Server, Storage Spaces Direct, and Windows Containers. 180-day trial.',
description:
'Legacy server release with Nano Server, Storage Spaces Direct, and Windows Containers support. Suitable for older environment compatibility testing.',
archs: ['x64'],
type: 'server',
requirements: {
cpu: '1.4 GHz 64-bit',
ram: '512 MB / 2 GB GUI',
disk: '32 GB',
},
},
{
slug: 'win11-ent',
name: 'Windows 11 Enterprise',
version: 'Evaluation',
build: 'Latest evaluation build',
seoDesc: 'Download Windows 11 Enterprise evaluation ISO from Microsoft. Includes BitLocker, Defender for Endpoint, and Intune management. 180-day trial.',
description:
'Full enterprise feature set including Windows Hello for Business, BitLocker, Microsoft Defender for Endpoint integration, and advanced management via Intune and Group Policy.',
archs: ['x64'],
type: 'enterprise',
requirements: {
cpu: '1 GHz, 2+ cores 64-bit',
ram: '4 GB',
disk: '64 GB',
note: 'TPM 2.0 required',
},
},
]
export const evalSlugSet = new Set(evalProducts.map(p => p.slug))

View file

@ -20,6 +20,7 @@ export default function DisclaimerPage() {
<meta property="og:title" content="Disclaimer | Windows ISO Downloader" />
<meta property="og:description" content="Legal disclaimer for MSDL. Not affiliated with Microsoft Corporation." />
<meta property="og:url" content={`${SITE_URL}/disclaimer`} />
<meta name="robots" content="noindex, follow" />
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div

View file

@ -0,0 +1,337 @@
import { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { motion, AnimatePresence } from 'motion/react'
import { ArrowLeft, Download, ExternalLink, AlertTriangle, Copy, Check, Server, ChevronDown } from 'lucide-react'
import { toast } from 'sonner'
import { evalProducts } from '../data/evalProducts'
import Aria2Tip from '../components/Aria2Tip'
import RelatedReleases from '../components/RelatedReleases'
import NotFoundPage from './NotFoundPage'
const API_BASE = import.meta.env.VITE_API_URL ?? 'http://localhost:3002'
const SITE_URL = 'https://msdl.tech-latest.com'
interface EvalLink {
arch: string
lang: string
url: string
}
const langNames: Record<string, string> = {
'en-us': 'English',
'zh-cn': 'Chinese (Simplified)',
'zh-tw': 'Chinese (Traditional)',
'fr-fr': 'French',
'de-de': 'German',
'it-it': 'Italian',
'ja-jp': 'Japanese',
'ko-kr': 'Korean',
'pt-br': 'Portuguese (Brazil)',
'ru-ru': 'Russian',
'es-es': 'Spanish',
'pl-pl': 'Polish',
'nl-nl': 'Dutch',
'sv-se': 'Swedish',
'tr-tr': 'Turkish',
}
function WinLogo({ className }: { className?: string }) {
return (
<svg viewBox="0 0 88 88" className={className} fill="currentColor" aria-hidden="true">
<path d="M0 12.402l35.687-4.86.016 34.423-35.67.203zm35.67 33.529l.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z" />
</svg>
)
}
export default function EvalDetailPage() {
const { productId } = useParams<{ productId: string }>()
const navigate = useNavigate()
const product = evalProducts.find(p => p.slug === productId)
const [links, setLinks] = useState<EvalLink[]>([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [copiedUrl, setCopiedUrl] = useState<string | null>(null)
const [moreOpen, setMoreOpen] = useState(false)
useEffect(() => {
if (!product) return
setIsLoading(true)
setError(null)
fetch(`${API_BASE}/evallinks?product=${product.slug}`)
.then(r => r.json())
.then(data => {
if (data.error) throw new Error(data.error)
setLinks(data.links ?? [])
})
.catch(err => setError(err.message))
.finally(() => setIsLoading(false))
}, [product?.slug])
async function handleCopy(url: string) {
try {
await navigator.clipboard.writeText(url)
setCopiedUrl(url)
toast.success('Link copied to clipboard')
setTimeout(() => setCopiedUrl(null), 2000)
} catch {
toast.error('Failed to copy link')
}
}
if (!product) return <NotFoundPage />
const primaryLink = links[0] ?? null
const otherLinks = links.slice(1)
const pageTitle = `${product.name} Evaluation ISO Download | Windows ISO Downloader`
const pageDesc = product.seoDesc
const canonical = `${SITE_URL}/product/${product.slug}`
const breadcrumbJsonLd = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: SITE_URL },
{ '@type': 'ListItem', position: 2, name: 'Enterprise & Server', item: `${SITE_URL}/eval` },
{ '@type': 'ListItem', position: 3, name: product.name, item: canonical },
],
}
const softwareJsonLd = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: `${product.name} Evaluation`,
operatingSystem: 'Windows',
applicationCategory: 'OperatingSystem',
url: canonical,
description: product.seoDesc,
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
}
return (
<>
<title>{pageTitle}</title>
<meta name="description" content={pageDesc} />
<link rel="canonical" href={canonical} />
<meta property="og:title" content={pageTitle} />
<meta property="og:description" content={pageDesc} />
<meta property="og:url" content={canonical} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(softwareJsonLd) }} />
<div className="max-w-2xl mx-auto px-5 pt-12 pb-10">
{/* Back */}
<button
onClick={() => window.history.state?.idx ? navigate(-1) : navigate('/eval')}
className="flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-300 mb-8 transition-colors"
>
<ArrowLeft size={15} />
Enterprise & Server
</button>
<motion.div
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
>
{/* Header */}
<div className="flex items-start gap-4 mb-6">
<div className="w-12 h-12 rounded-xl bg-white/5 border border-white/8 flex items-center justify-center flex-shrink-0">
{product.type === 'server'
? <Server className="w-6 h-6 text-white/50" />
: <WinLogo className="w-6 h-6 text-white/50" />
}
</div>
<div className="min-w-0">
<h1 className="text-2xl font-bold text-white leading-tight tracking-tight">
{product.name}
</h1>
<p className="text-[12px] font-mono text-zinc-500 mt-0.5">{product.version}</p>
<div className="flex gap-1.5 mt-2">
{product.archs.map(arch => (
<span key={arch} className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded bg-white/5 border border-white/8 text-zinc-500">
{arch}
</span>
))}
<span className="text-[10px] font-mono font-semibold px-2 py-0.5 rounded-full bg-amber-500/10 border border-amber-500/20 text-amber-400">
EVAL
</span>
</div>
</div>
</div>
{/* Eval notice */}
<div className="flex items-start gap-2.5 p-3.5 rounded-xl border border-amber-500/15 bg-amber-500/6 mb-5">
<AlertTriangle size={14} className="text-amber-400/80 mt-0.5 flex-shrink-0" />
<p className="text-[11px] text-amber-400/70 leading-relaxed">
<strong className="text-amber-400">180-day evaluation only.</strong> Not for production use. A valid license is required for activation beyond the trial period.
</p>
</div>
{/* Download card */}
<div className="rounded-2xl border border-white/7 bg-[#111113] p-6 mb-4">
<p className="text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600 mb-4">
Download
</p>
{isLoading ? (
<div className="space-y-2.5">
<div className="h-11 bg-white/5 rounded-xl animate-pulse" />
<div className="h-8 w-36 bg-white/4 rounded-lg animate-pulse" />
</div>
) : error ? (
<div className="py-4 text-center">
<p className="text-sm text-red-400 mb-1">Failed to load download links</p>
<p className="text-xs text-zinc-600">{error}</p>
</div>
) : primaryLink ? (
<div className="space-y-3">
{/* Primary link */}
<div className="flex items-center gap-2">
<a
href={primaryLink.url}
target="_blank"
rel="noopener noreferrer"
className="flex-1 flex items-center gap-2 px-4 py-3 rounded-xl bg-blue-500/10 border border-blue-500/20 text-blue-400 hover:bg-blue-500/15 hover:border-blue-500/30 transition-all text-sm font-medium"
>
<Download size={14} />
Download {primaryLink.arch}
{primaryLink.lang && (
<span className="text-blue-400/60 font-normal">
· {langNames[primaryLink.lang] ?? primaryLink.lang}
</span>
)}
<ExternalLink size={12} className="ml-auto opacity-60" />
</a>
<button
onClick={() => handleCopy(primaryLink.url)}
className={`p-3 rounded-xl border transition-all flex-shrink-0 ${
copiedUrl === primaryLink.url
? 'border-green-500/30 bg-green-500/10 text-green-400'
: 'border-white/8 bg-white/4 text-zinc-500 hover:text-white hover:border-white/15'
}`}
title="Copy download link"
>
{copiedUrl === primaryLink.url ? <Check size={14} /> : <Copy size={14} />}
</button>
</div>
{/* More languages */}
{otherLinks.length > 0 && (
<div>
<button
onClick={() => setMoreOpen(o => !o)}
className="flex items-center gap-1.5 text-xs text-zinc-600 hover:text-zinc-400 transition-colors"
>
<ChevronDown
size={12}
className={`transition-transform duration-200 ${moreOpen ? 'rotate-180' : ''}`}
/>
{otherLinks.length} more language{otherLinks.length > 1 ? 's' : ''}
</button>
<AnimatePresence>
{moreOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div className="space-y-1.5 mt-2.5">
{otherLinks.map(link => (
<div key={link.url} className="flex items-center gap-2">
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="flex-1 flex items-center gap-2 px-3 py-2 rounded-lg bg-white/4 border border-white/6 text-zinc-400 hover:bg-white/7 hover:border-white/10 hover:text-white transition-all text-xs"
>
<Download size={11} />
{link.arch}
{link.lang && (
<span className="text-zinc-500">
· {langNames[link.lang] ?? link.lang}
</span>
)}
<ExternalLink size={10} className="ml-auto opacity-50" />
</a>
<button
onClick={() => handleCopy(link.url)}
className={`px-2.5 py-2 rounded-lg border text-[10px] font-mono transition-all flex-shrink-0 ${
copiedUrl === link.url
? 'border-green-500/30 bg-green-500/10 text-green-400'
: 'border-white/8 bg-white/4 text-zinc-500 hover:text-white hover:border-white/15'
}`}
>
{copiedUrl === link.url ? '✓' : 'Copy'}
</button>
</div>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
)}
<p className="text-[10px] text-zinc-600 pt-1">
Direct from Microsoft's CDN · Evaluation only requires activation after 180 days
</p>
</div>
) : (
<p className="text-sm text-zinc-500 text-center py-4">No download links available.</p>
)}
</div>
{/* System requirements */}
<div className="rounded-xl border border-white/7 bg-[#111113] p-4 mb-4">
<p className="text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600 mb-3">
System Requirements
</p>
<div className="grid grid-cols-3 gap-3">
<div className="flex flex-col gap-1">
<span className="text-[11px] text-zinc-600">Processor</span>
<span className="text-sm font-semibold text-white">{product.requirements.cpu}</span>
</div>
<div className="flex flex-col gap-1">
<span className="text-[11px] text-zinc-600">RAM</span>
<span className="text-sm font-semibold text-white">{product.requirements.ram}</span>
</div>
<div className="flex flex-col gap-1">
<span className="text-[11px] text-zinc-600">Storage</span>
<span className="text-sm font-semibold text-white">{product.requirements.disk}</span>
</div>
</div>
{product.requirements.note && (
<div className="mt-3 pt-3 border-t border-white/5 flex items-center gap-2">
<AlertTriangle size={12} className="text-amber-500 flex-shrink-0" />
<span className="text-[11px] text-amber-500/80">{product.requirements.note}</span>
</div>
)}
</div>
{/* About */}
<div className="rounded-xl border border-white/7 bg-[#111113] p-4">
<p className="text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600 mb-2">
About this release
</p>
<p className="text-sm text-zinc-400 leading-relaxed">{product.description}</p>
</div>
{/* Aria2 tip — outside box, same as consumer page */}
<Aria2Tip />
{/* Also available — outside box, same as consumer page */}
<RelatedReleases
current={product.slug}
items={evalProducts.map(p => ({ id: p.slug, label: p.name }))}
/>
</motion.div>
</div>
</>
)
}

View file

@ -0,0 +1,135 @@
import { Link } from 'react-router-dom'
import { motion } from 'motion/react'
import { ArrowRight, AlertTriangle, Server } from 'lucide-react'
import { evalProducts } from '../data/evalProducts'
const SITE_URL = 'https://msdl.tech-latest.com'
const itemListJsonLd = {
'@context': 'https://schema.org',
'@type': 'ItemList',
name: 'Windows Server & Enterprise Evaluation ISOs',
description: 'Official evaluation ISOs for Windows Server and Enterprise editions, direct from Microsoft.',
itemListElement: evalProducts.map((p, i) => ({
'@type': 'ListItem',
position: i + 1,
name: p.name,
url: `${SITE_URL}/product/${p.slug}`,
})),
}
const typeColors = {
server: 'text-violet-400 bg-violet-500/10 border-violet-500/20',
enterprise: 'text-blue-400 bg-blue-500/10 border-blue-500/20',
} as const
const typeLabel = {
server: 'Server',
enterprise: 'Enterprise',
} as const
const container = {
hidden: {},
show: { transition: { staggerChildren: 0.06 } },
}
const cardVariant = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { type: 'spring' as const, stiffness: 280, damping: 28 } },
}
export default function EvalPage() {
return (
<>
<title>Enterprise & Server ISOs | Windows ISO Downloader</title>
<meta name="description" content="Download Windows Server 2025, 2022, 2019, 2016 and Windows 11 Enterprise evaluation ISOs directly from Microsoft's CDN." />
<link rel="canonical" href={`${SITE_URL}/eval`} />
<meta property="og:title" content="Enterprise & Server ISOs | Windows ISO Downloader" />
<meta property="og:description" content="Download Windows Server and Enterprise evaluation ISOs directly from Microsoft's CDN." />
<meta property="og:url" content={`${SITE_URL}/eval`} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(itemListJsonLd) }} />
<div className="max-w-4xl mx-auto px-5 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
{/* Header */}
<div className="mb-6">
<p className="text-[11px] font-mono font-semibold tracking-[0.2em] uppercase text-violet-400 mb-3">
Enterprise & Server
</p>
<h1 className="text-3xl font-bold text-white mb-2">Evaluation ISOs</h1>
<p className="text-zinc-400 text-sm leading-relaxed max-w-lg">
Official evaluation editions from Microsoft's evalcenter. Direct links from Microsoft's CDN no session, no registration.
</p>
</div>
{/* Eval notice */}
<div className="flex items-start gap-3 p-4 rounded-xl border border-amber-500/15 bg-amber-500/6 mb-8">
<AlertTriangle size={15} className="text-amber-400/80 mt-0.5 flex-shrink-0" />
<p className="text-xs text-amber-400/70 leading-relaxed">
<strong className="text-amber-400">Evaluation editions only.</strong> These ISOs are time-limited 180-day trials intended for testing and evaluation not for production use. A valid license is required for activation beyond the trial period.
</p>
</div>
{/* Product grid */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-3"
variants={container}
initial="hidden"
animate="show"
>
{evalProducts.map(product => (
<motion.div key={product.slug} variants={cardVariant}>
<Link to={`/product/${product.slug}`} className="block outline-none group">
<motion.div
whileHover={{ y: -3, scale: 1.005 }}
transition={{ type: 'spring', stiffness: 400, damping: 25 }}
className="relative rounded-2xl border border-white/7 bg-[#111113] p-6
hover:border-white/13 hover:shadow-2xl hover:shadow-black/40
transition-colors duration-300"
>
{/* Header row */}
<div className="flex items-start justify-between mb-4">
<div className="w-9 h-9 rounded-lg bg-white/5 border border-white/8 flex items-center justify-center">
<Server className="w-4 h-4 text-white/50" />
</div>
<span className={`text-[10px] font-semibold px-2 py-0.5 rounded-full border ${typeColors[product.type]}`}>
{typeLabel[product.type]}
</span>
</div>
{/* Title */}
<h2 className="text-base font-semibold text-white leading-tight">{product.name}</h2>
<p className="text-[11px] font-mono text-zinc-500 mt-0.5 mb-3">{product.version}</p>
<p className="text-sm text-zinc-400 leading-relaxed mb-4 line-clamp-2">{product.description}</p>
{/* Footer */}
<div className="flex items-center justify-between">
<div className="flex gap-1.5">
{product.archs.map(arch => (
<span key={arch} className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded bg-white/5 border border-white/8 text-zinc-500">
{arch}
</span>
))}
<span className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded bg-amber-500/8 border border-amber-500/20 text-amber-500/70">
EVAL
</span>
</div>
<div className="flex items-center gap-1 text-xs text-zinc-600 group-hover:text-zinc-400 transition-colors">
<span>Get links</span>
<ArrowRight size={11} className="group-hover:translate-x-0.5 transition-transform" />
</div>
</div>
</motion.div>
</Link>
</motion.div>
))}
</motion.div>
</motion.div>
</div>
</>
)
}

View file

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { motion } from 'motion/react'
import { ArrowRight } from 'lucide-react'
import { ArrowRight, Server } from 'lucide-react'
import ProductCard from '../components/ProductCard'
import StatsBar from '../components/StatsBar'
import HowItWorks from '../components/HowItWorks'
@ -66,7 +66,7 @@ const jsonLd = {
applicationCategory: 'UtilitiesApplication',
operatingSystem: 'Web',
url: SITE_URL,
description: 'Download official Microsoft Windows ISO files for Windows 11, 10, and 8.1. Direct links from Microsoft CDN. Free, no registration.',
description: 'Download official Microsoft Windows ISO files for Windows 11, 10, 8.1, and Windows Server. Direct links from Microsoft CDN. Free, no registration.',
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
}
@ -161,6 +161,34 @@ export default function HomePage() {
))}
</motion.div>
{/* Enterprise & Server CTA */}
<motion.div
className="mb-4"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.45 }}
>
<Link
to="/eval"
className="group flex items-center justify-between w-full px-5 py-3.5 rounded-xl border border-violet-500/15 bg-violet-500/5 hover:border-violet-500/25 hover:bg-violet-500/8 transition-all duration-200"
>
<div className="flex items-center gap-3">
<div className="w-7 h-7 rounded-lg bg-violet-500/10 border border-violet-500/20 flex items-center justify-center flex-shrink-0">
<Server size={14} className="text-violet-400" />
</div>
<div>
<p className="text-sm font-medium text-white/80 group-hover:text-white transition-colors leading-tight">
Enterprise & Server ISOs
</p>
<p className="text-[11px] text-zinc-600 mt-0.5">
Server 2025, 2022, 2019, 2016 · Win 11 Enterprise · Eval editions
</p>
</div>
</div>
<ArrowRight size={14} className="text-zinc-600 group-hover:text-violet-400 group-hover:translate-x-0.5 transition-all" />
</Link>
</motion.div>
{/* Browse all CTA */}
<motion.div
className="text-center"

View file

@ -20,6 +20,7 @@ export default function PrivacyPolicyPage() {
<meta property="og:title" content="Privacy Policy | Windows ISO Downloader" />
<meta property="og:description" content="Privacy policy for MSDL. We log no data, use no trackers, and proxy no files." />
<meta property="og:url" content={`${SITE_URL}/privacy-policy`} />
<meta name="robots" content="noindex, follow" />
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div

View file

@ -75,7 +75,7 @@ export default function ProductDetailPage() {
: hasCatalogError
? 'Failed to load product catalog.'
: productName
? `Download official ISO image for ${productName}. Direct links pulled securely from Microsoft CDN servers.`
? `Download the official ${productName} ISO directly from Microsoft's CDN. No registration, no proxy — real-time signed links.`
: ''
const canonicalUrl = `${SITE_URL}/product/${productId}`
@ -90,6 +90,17 @@ export default function ProductDetailPage() {
],
} : null
const softwareJsonLd = productName && !isNotFound && !hasCatalogError ? {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: productName,
operatingSystem: 'Windows',
applicationCategory: 'OperatingSystem',
url: canonicalUrl,
description: pageDescription,
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
} : null
// Load product name + build string
useEffect(() => {
setBuildStr('')
@ -209,8 +220,11 @@ export default function ProductDetailPage() {
{breadcrumbJsonLd && (
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
)}
{softwareJsonLd && (
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(softwareJsonLd) }} />
)}
<div className="max-w-xl mx-auto px-5 pt-12 pb-10">
<div className="max-w-2xl mx-auto px-5 pt-12 pb-10">
{/* Back */}
<button
onClick={() => window.history.state?.idx ? navigate(-1) : navigate('/')}

View file

@ -36,14 +36,14 @@ export default function ProductsPage() {
return (
<>
<title>All Windows Releases | Windows ISO Downloader</title>
<title>All Windows ISO Releases | Windows ISO Downloader</title>
<meta name="description" content="Browse all official Microsoft Windows ISO releases available for direct download. Windows 11, 10, and 8.1." />
<link rel="canonical" href={`${SITE_URL}/products`} />
<meta property="og:title" content="All Windows ISO Releases | Windows ISO Downloader" />
<meta property="og:description" content="Browse all official Microsoft Windows ISO releases available for direct download." />
<meta property="og:url" content={`${SITE_URL}/products`} />
<div className="max-w-3xl mx-auto px-4 pt-12 pb-10">
<div className="max-w-4xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}