NFT metadata images not displaying on marketplaces and wallets

I built an ERC721 smart contract and deployed it to Polygon network to create 10 digital collectibles. The artwork files are hosted on my personal server instead of using IPFS for this test project. I also set up the metadata JSON files properly.

I successfully compiled and deployed everything through Remix IDE and minted all 10 tokens without any errors. However, I’m facing the same issue I had before - the images don’t appear anywhere. They’re missing from MetaMask when I import the token address, don’t show on PolygonScan, and OpenSea just displays generic placeholder icons.

Here’s my contract code:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract DigitalCardsV2 is ERC721URIStorage, Ownable {
    uint256 public tokenCounter;
    uint256 public constant TOTAL_SUPPLY = 10;
    string public metadataBaseURI;

    event TokenCreated(address indexed owner, uint256 tokenId, string metadataURI);

    constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) Ownable(msg.sender) {
        metadataBaseURI = "https://15.41.85.123/metadata/";
    }

    function createToken(address _recipient) external onlyOwner {
        require(_recipient != address(0), "Invalid recipient address");
        require(tokenCounter < TOTAL_SUPPLY, "All tokens have been minted");

        uint256 nextTokenId = ++tokenCounter;
        _safeMint(_recipient, nextTokenId);

        string memory fullURI = string(abi.encodePacked(metadataBaseURI, Strings.toString(nextTokenId), ".json"));
        _setTokenURI(nextTokenId, fullURI);

        emit TokenCreated(_recipient, nextTokenId, fullURI);
    }

    function updateBaseURI(string memory _newBaseURI) external onlyOwner {
        require(bytes(_newBaseURI).length > 0, "URI cannot be empty");
        metadataBaseURI = _newBaseURI;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return metadataBaseURI;
    }

    receive() external payable {}
    fallback() external payable {}
}

Can anyone help me figure out what’s wrong? I want these images to display correctly in wallets and NFT marketplaces instead of showing placeholder graphics.

your metadata json files might be missing the required fields or have wrong formatting. opensea specifically needs “name”, “description”, and “image” fields spelled exactly like that. also try adding “external_url” field even if its empty. the ip address hosting could work but make sure your server sends proper content-type headers for json files (application/json). another quick test - try viewing source on polygonscan for your token and see if the metadata url actually returns valid json when clicked.

I ran into this exact problem when I was testing my first NFT collection. The issue is almost certainly that you’re hosting on a personal server with an IP address instead of a proper domain with HTTPS. Most marketplaces and wallets have strict CORS policies and require secure connections to load external images. When I switched from my local server setup to a proper hosting solution with SSL certificates, everything started displaying immediately. Your contract code looks fine - the problem is definitely on the hosting side. Even for testing purposes, consider using a service like Netlify or Vercel for your metadata and images, they provide HTTPS by default and have better reliability than personal servers. Also double-check that your server allows cross-origin requests from the domains that need to access your files. Without proper CORS headers, the requests will be blocked even if the URLs are accessible directly in a browser.

hmm interesting issue you got there! I’m curious - when you say the images dont show up, have you actually tried accessing those metadata URLs directly in your browser? like can you visit https://15.41.85.123/metadata/1.json and see the json response with the image url?

also what does your actual metadata json structure look like? sometimes the problem isnt the hosting but the way the metadata is formatted. different marketplaces can be picky about the exact field names - some want “image” while others might expect “image_url” or “animation_url” depending on the file type.

another thing im wondering about - is your server actually running and accessible from outside your network? that IP address needs to be publicly reachable, not just from your local machine. you might want to test this by asking a friend to try accessing one of those metadata urls from their computer.

btw have you checked the browser console when you’re looking at opensea or polygonscan? sometimes there are error messages there that give you hints about whats going wrong with loading the images. could be anything from network timeouts to content type issues.

what file formats are you using for the images? and are they properly referenced in your metadata json files?