magento2 – Magento 2 admin redirect after adding varnish

I just added varnish cache to my site. Before I added it, I was able to get onto the admin panel. After adding it, though, trying to get to the admin panel results in an infinite redirect loop.

It redirects with the following http codes:

302->307->302->307…

It does this on all browsers even when I clear browser cache.

Here is my varnish config:

# VCL version 5.0 is not supported so it should be 4.0 even though actually used Varnish version is 6
vcl 4.0;

import std;
# The minimal Varnish version is 6.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'SSL-OFFLOADED: https'

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .first_byte_timeout = 600s;
}

acl purge {
    "localhost";
}

sub vcl_recv {
    if (req.method == "PURGE") {
        if (client.ip !~ purge) {
            return (synth(405, "Method not allowed"));
        }
        # To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header
        # has been added to the response in your backend server config. This is used, for example, by the
        # capistrano-magento2 gem for purging old content from varnish during it's deploy routine.
        if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) {
            return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required"));
        }
        if (req.http.X-Magento-Tags-Pattern) {
          ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
        }
        if (req.http.X-Pool) {
          ban("obj.http.X-Pool ~ " + req.http.X-Pool);
        }
        return (synth(200, "Purged"));
    }

    if (req.method != "GET" &&
        req.method != "HEAD" &&
        req.method != "PUT" &&
        req.method != "POST" &&
        req.method != "TRACE" &&
        req.method != "OPTIONS" &&
        req.method != "DELETE") {
          /* Non-RFC2616 or CONNECT which is weird. */
          return (pipe);
    }

    # We only deal with GET and HEAD by default
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Bypass shopping cart, checkout and search requests
    if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") {
        return (pass);
    }

    # Bypass health check requests
    if (req.url ~ "/pub/health_check.php") {
        return (pass);
    }

    # Set initial grace period usage status
    set req.http.grace = "none";

    # normalize url in case of leading HTTP scheme and domain
    set req.url = regsub(req.url, "^http(s)?://", "");

    # collect all cookies
    std.collect(req.http.Cookie);

    # Compression filter. See https://www.varnish-cache.org/trac/wiki/FAQ/Compression
    if (req.http.Accept-Encoding) {
        if (req.url ~ ".(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
            # No point in compressing these
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            # unknown algorithm
            unset req.http.Accept-Encoding;
        }
    }

    # Remove all marketing get parameters to minimize the cache objects
    if (req.url ~ "(?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_(a-z)+|utm_(a-z)+|_bta_(a-z)+)=") {
        set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|fbclid|mc_(a-z)+|utm_(a-z)+|_bta_(a-z)+)=(-_A-z0-9+()%.)+&?", "");
        set req.url = regsub(req.url, "(?|&)+$", "");
    }

    # Static files caching
    if (req.url ~ "^/(pub/)?(media|static)/") {
        # Static files should not be cached by default
        return (pass);

        # But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
        #unset req.http.Https;
        #unset req.http.SSL-OFFLOADED;
        #unset req.http.Cookie;
    }

    return (hash);
}

sub vcl_hash {
    if (req.http.cookie ~ "X-Magento-Vary=") {
        hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=((^;)+);*.*$", "1"));
    }

    # For multi site configurations to not cache each other's content
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }

    # To make sure http users don't see ssl warning
    if (req.http.SSL-OFFLOADED) {
        hash_data(req.http.SSL-OFFLOADED);
    }
    

    if (req.url ~ "/graphql") {
        call process_graphql_headers;
    }
}

sub process_graphql_headers {
    if (req.http.Store) {
        hash_data(req.http.Store);
    }
    if (req.http.Content-Currency) {
        hash_data(req.http.Content-Currency);
    }
}

sub vcl_backend_response {

    set beresp.grace = 3d;

    if (beresp.http.content-type ~ "text") {
        set beresp.do_esi = true;
    }

    if (bereq.url ~ ".js$" || beresp.http.content-type ~ "text") {
        set beresp.do_gzip = true;
    }

    if (beresp.http.X-Magento-Debug) {
        set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
    }

    # cache only successfully responses and 404s
    if (beresp.status != 200 && beresp.status != 404) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
        return (deliver);
    } elsif (beresp.http.Cache-Control ~ "private") {
        set beresp.uncacheable = true;
        set beresp.ttl = 86400s;
        return (deliver);
    }

    # validate if we need to cache it and prevent from setting cookie
    if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
        unset beresp.http.set-cookie;
    }

   # If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
   if (beresp.ttl <= 0s ||
       beresp.http.Surrogate-control ~ "no-store" ||
       (!beresp.http.Surrogate-Control &&
       beresp.http.Cache-Control ~ "no-cache|no-store") ||
       beresp.http.Vary == "*") {
        # Mark as Hit-For-Pass for the next 2 minutes
        set beresp.ttl = 120s;
        set beresp.uncacheable = true;
    }

    return (deliver);
}

sub vcl_deliver {
    if (resp.http.X-Magento-Debug) {
        if (resp.http.x-varnish ~ " ") {
            set resp.http.X-Magento-Cache-Debug = "HIT";
            set resp.http.Grace = req.http.grace;
        } else {
            set resp.http.X-Magento-Cache-Debug = "MISS";
        }
    } else {
        unset resp.http.Age;
    }

    # Not letting browser to cache non-static files.
    if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
        set resp.http.Pragma = "no-cache";
        set resp.http.Expires = "-1";
        set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";
    }

    unset resp.http.X-Magento-Debug;
    unset resp.http.X-Magento-Tags;
    unset resp.http.X-Powered-By;
    unset resp.http.Server;
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.Link;
}

sub vcl_hit {
    if (obj.ttl >= 0s) {
        # Hit within TTL period
        return (deliver);
    }
    if (std.healthy(req.backend_hint)) {
        if (obj.ttl + 300s > 0s) {
            # Hit after TTL expiration, but within grace period
            set req.http.grace = "normal (healthy server)";
            return (deliver);
        } else {
            # Hit after TTL and grace expiration
            return (restart);
        }
    } else {
        # server is not healthy, retrieve from cache
        set req.http.grace = "unlimited (unhealthy server)";
        return (deliver);
    }
}

And Here is Nginx config (Actual site name replaced with example.com):

## Example configuration:
upstream fastcgi_backend {
    # use tcp connection
    # server  127.0.0.1:9000;
    # or socket
    server   unix:/var/run/php/php7.3-fpm.sock;
 }
# server {
#    listen 80;
#    server_name mage.dev;
#    set $MAGE_ROOT /var/www/magento2;
#    set $MAGE_DEBUG_SHOW_ARGS 0;
#    include /vagrant/magento2/nginx.conf.sample;
# }
#
## Optional override of deployment mode. We recommend you use the
## command 'bin/magento deploy:mode:set' to switch modes instead.
##
## set $MAGE_MODE default; # or production or developer
##
## If you set MAGE_MODE in server config, you must pass the variable into the
## PHP entry point blocks, which are indicated below. You can pass
## it in using:
##
## fastcgi_param  MAGE_MODE $MAGE_MODE;
##
## In production mode, you should uncomment the 'expires' directive in the /static/ location block

# Modules can be loaded only at the very beginning of the Nginx config file, please move the line below to the main config file
# load_module /etc/nginx/modules/ngx_http_image_filter_module.so;

server {

listen 8080;

server_name example.com *.example.com;

set $MAGE_ROOT /var/www/html/example.com;
set $MAGE_DEBUG_SHOW_ARGS 0;

root $MAGE_ROOT/pub;

index index.php;
autoindex off;
charset UTF-8;
error_page 404 403 = /errors/404.php;
#add_header "X-UA-Compatible" "IE=Edge";


# Deny access to sensitive files
location /.user.ini {
    deny all;
}

# PHP entry point for setup application
location ~* ^/setup($|/) {
    root $MAGE_ROOT;
    location ~ ^/setup/index.php {
        fastcgi_pass   fastcgi_backend;

        fastcgi_param  PHP_FLAG  "session.auto_start=off n suhosin.session.cryptua=off";
        fastcgi_param  PHP_VALUE "memory_limit=756M n max_execution_time=600";
        fastcgi_read_timeout 600s;
        fastcgi_connect_timeout 600s;

        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location ~ ^/setup/(?!pub/). {
        deny all;
    }

    location ~ ^/setup/pub/ {
        add_header X-Frame-Options "SAMEORIGIN";
    }
}

# PHP entry point for update application
location ~* ^/update($|/) {
    root $MAGE_ROOT;

    location ~ ^/update/index.php {
        fastcgi_split_path_info ^(/update/index.php)(/.+)$;
        fastcgi_pass   fastcgi_backend;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO        $fastcgi_path_info;
        include        fastcgi_params;
    }

    # Deny everything but index.php
    location ~ ^/update/(?!pub/). {
        deny all;
    }

    location ~ ^/update/pub/ {
        add_header X-Frame-Options "SAMEORIGIN";
    }
}

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}

location /pub/ {
    location ~ ^/pub/media/(downloadable|customer|import|custom_options|theme_customization/.*.xml) {
        deny all;
    }
    alias $MAGE_ROOT/pub/;
    add_header X-Frame-Options "SAMEORIGIN";
}

location /static/ {
    # Uncomment the following line in production mode
    # expires max;

    # Remove signature of the static files that is used to overcome the browser cache
    location ~ ^/static/versiond*/ {
        rewrite ^/static/versiond*/(.*)$ /static/$1 last;
    }

    location ~* .(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|eot|ttf|otf|woff|woff2|html|json|webmanifest)$ {
        add_header Cache-Control "public";
        add_header X-Frame-Options "SAMEORIGIN";
        expires +1y;

        if (!-f $request_filename) {
            rewrite ^/static/(versiond*/)?(.*)$ /static.php?resource=$2 last;
        }
    }
    location ~* .(zip|gz|gzip|bz2|csv|xml)$ {
        add_header Cache-Control "no-store";
        add_header X-Frame-Options "SAMEORIGIN";
        expires    off;

        if (!-f $request_filename) {
           rewrite ^/static/(versiond*/)?(.*)$ /static.php?resource=$2 last;
        }
    }
    if (!-f $request_filename) {
        rewrite ^/static/(versiond*/)?(.*)$ /static.php?resource=$2 last;
    }
    add_header X-Frame-Options "SAMEORIGIN";
}

location /media/ {

## The following section allows to offload image resizing from Magento instance to the Nginx.
## Catalog image URL format should be set accordingly.
## See https://docs.magento.com/user-guide/configuration/general/web.html#url-options
#   location ~* ^/media/catalog/.* {
#
#       # Replace placeholders and uncomment the line below to serve product images from public S3
#       # See examples of S3 authentication at https://github.com/anomalizer/ngx_aws_auth
#       # resolver 8.8.8.8;
#       # proxy_pass https://<bucket-name>.<region-name>.amazonaws.com;
#
#       set $width "-";
#       set $height "-";
#       if ($arg_width != '') {
#           set $width $arg_width;
#       }
#       if ($arg_height != '') {
#           set $height $arg_height;
#       }
#       image_filter resize $width $height;
#       image_filter_jpeg_quality 90;
#   }

    try_files $uri $uri/ /get.php$is_args$args;

    location ~ ^/media/theme_customization/.*.xml {
        deny all;
    }

    location ~* .(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|eot|ttf|otf|woff|woff2)$ {
        add_header Cache-Control "public";
        add_header X-Frame-Options "SAMEORIGIN";
        expires +1y;
        try_files $uri $uri/ /get.php$is_args$args;
    }
    location ~* .(zip|gz|gzip|bz2|csv|xml)$ {
        add_header Cache-Control "no-store";
        add_header X-Frame-Options "SAMEORIGIN";
        expires    off;
        try_files $uri $uri/ /get.php$is_args$args;
    }
    add_header X-Frame-Options "SAMEORIGIN";
}

location /media/customer/ {
    deny all;
}

location /media/downloadable/ {
    deny all;
}

location /media/import/ {
    deny all;
}

location /media/custom_options/ {
    deny all;
}

location /errors/ {
    location ~* .xml$ {
        deny all;
    }
}

# PHP entry point for main application
location ~ ^/(index|get|static|errors/report|errors/404|errors/503|health_check).php$ {
    try_files $uri =404;
    fastcgi_pass   fastcgi_backend;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_param HTTPS on;
    fastcgi_param  PHP_FLAG  "session.auto_start=off n suhosin.session.cryptua=off";
    fastcgi_param  PHP_VALUE "memory_limit=756M n max_execution_time=18000";
    fastcgi_read_timeout 600s;
    fastcgi_connect_timeout 600s;

    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

gzip on;
gzip_disable "msie6";

gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types
    text/plain
    text/css
    text/js
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    image/svg+xml;
gzip_vary on;

# Banned locations (only reached if the earlier PHP entry point regexes don't match)
location ~* (.php$|.phtml$|.htaccess$|.git) {
    deny all;
}

}

server {
    listen 443 ssl; # managed by Certbot
    
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

       location / {
       proxy_pass http://127.0.0.1:6081;
       proxy_set_header Host               $http_host;
       proxy_set_header X-Forwarded-Host   $http_host;
       proxy_set_header X-Real-IP          $remote_addr;
       proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto  https;
       proxy_set_header X-Forwarded-Port   443;
       proxy_buffer_size                   128k;
       proxy_buffers                       4 256k;
       proxy_busy_buffers_size             256k;
       fastcgi_buffer_size                 32k;
       fastcgi_buffers                     4 32k;
   }
}

server {
 listen 80;
     location / {
          proxy_pass http://127.0.0.1:6081/;
          proxy_set_header Host $host;  
          proxy_set_header X-Forwarded-Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Ssl-Offloaded "1";
      proxy_set_header      X-Forwarded-Port 443;
      proxy_set_header X-Forwarded-Proto https;
      proxy_set_header X-Forwarded-Ssl on;

}
}

I have tried the following things, none of which worked:

https://mrvts.wordpress.com/2018/12/17/magento-2-nginx-varnish-full-page-cache-backend-admin-redirect-loop-why-and-how-to-fix/
https://stackoverflow.com/questions/10915731/infinite-redirection-loop-in-varnish-cache

It only happens for the admin panel. The frontend works as expected.

woocommerce offtopic – Admin access for custom user capabilities

I’ve got a site with a lot of custom user roles and capabilities, and I’m trying to allow users with a new capability ‘admin_panel_access` (set up via User Role Editor) to access the admin dashboard.

No matter what I try – several different versions of access in functions.php (see below for my latest) – users with that capability cannot access /wp-admin at all.

function restrict_admin(){
    if ( ! current_user_can( 'admin_panel_access' ) ) {
        wp_die( __('You are not allowed to access this part of the site') );
    }
}
add_action( 'admin_init', 'restrict_admin', 1 );

I’ve narrowed it down by changing theme and disabling plugins one by one, and the culprit is WooCommerce. For some reason, when I activate WooCommerce it stops roles with custom capabilities (other than edit_posts) being able to access.

Any clues or ideas would be fantastic. 5 hours of searching and testing is enough to drive anyone to madness!

Thanks.

Not able to access WP Admin, it says “Sorry, you are not allowed to access this page.”

All of a sudden we are not able to get to the admin panel of the website. Once I log into /wp-login.php it redirects me to the homepage or /wp-admin (depending on whether I visited wp-admin in logged out state or not)

WordPress version: 5.6.2

When it does redirect to /wp-admin I see:

“Sorry, you are not allowed to access this page.”

My first guess was that someone (maybe another user with administrator access) changed the roles. That wasn’t the case, I confirmed by looking at the database tables and the users had the wp_capabilities that corresponded to being an admin.

Then I tried a bunch of other stuffs and none worked:

  1. Created a new user using wp_create_user, added administrator capabilities, logged into that account from a private browsing window. Same.

  2. Went to the WordPress git repo searched for where “Sorry, you are not allowed to access this page.” appeared, it did at many places so I added random string of text in some files to see exactly which one, it came from the file wp-admin/includes/menu.php in the last lines:

    if ( ! user_can_access_admin_page() ) {

  3. I thought maybe user_can_access_admin_page() returned wrong information for some reason, so I used a page template to output some data like so

if ( current_user_can('administrator') ) {
    echo "Yes, admin";
}

if ( user_can_access_admin_page() ) {
    echo "Can access admin";
} else {
    echo "Can't access admin";
}

Both returned true, yet the check on menu.php fails. Not sure why.

  1. Replaced wp-admin and wp-includes folder with fresh copies downloaded from WordPress org.

  2. Renamed the theme and plugin folders to rule out the problem coming from a plugin or theme. Not the case, it still was the same.

  3. Checked the error log of Apache2 as well as enabled debugging log for WP to look for any related error, none found. Nothing special, all the usual PHP notices that I have seen before.

  4. At one point I noticed the SSL certificate expired a month ago (problem started yesterday though) so just to be sure I renewed that as well, still the same.

Anybody who have an idea on what might be wrong would be a great help!

magento2 – Magento 2 : Acl admin controller

I can see below code in file

use MagentoBackendAppAction;

/**
 * Abstract assignment controller
 */
abstract class Assignment extends Action
{
    /**
     * Authorization level of a basic admin session
     *
     * @see _isAllowed()
     */
    const ADMIN_RESOURCE = 'Vendor_Catalog::import_product_category_assignment';
}

This class is extended by controller

class Index extends Assignment

I am not sure what’s the use of “const ADMIN_RESOURCE = 'Vendor_Catalog::import_product_category_assignment';” there

php – Updating column fields from admin dashboard PDO

Hi, i got this code in PHP and i was in need for updating those collumns from the dashboard. The tables are being created in the while loop, but i cant figure out how to update these values from the button.
Any ideas? im begginer at PDO.

<?php
require_once('connection.php');

try{
    $stmte = $db->query("SELECT * FROM tb_agendamento ");
    $executa = $stmte->execute();
?>
<div class="container">
    <table align="center" class="table table-bordered table-responsive table-hover">
    <tr><td><b>Código de agendamento</td><td><b>Código do cliente</td><td><b>Data</td><td><b>Horário</td><td colspan="2" align="center">Ação</td></tr>

<?php
    if($executa){
        while($reg = $stmte->fetch(PDO::FETCH_OBJ)){ // Para recuperar um ARRAY utilize PDO::FETCH_ASSOC 
?>
            <tr><td><?=$reg->cd_agendamento?></td>
            <td><?=$reg->cd_cliente?></td>
            <td><?=$reg->dt_visita?></td>
            <td><?=$reg->hr_inicio?></td>
            <td><div class="form-group">
            <button type="submit" name="aceitar" class="btn btn-success btn-lg">Aceitar</button>
            </div></td>
            <td><div class="form-group">
            <button type="submit" name="negar" class="btn btn-danger btn-lg ">Negar</button>
            </div></td></tr>

            <?php if(isset($_POST('aceitar')))
{      
    
 echo "<script>alert('test.');</script>";

}       ?>

           
<?php
       }
       print '</table>';
print '</div>';
    }
}catch(PDOException $e){
      echo $e->getMessage();
}

if(isset($_POST('aceitar')))
{      
    
 echo "<script>alert('test');</script>";

}       
if(isset($_POST('negar')))
{
 echo "<script type='text/javascript'> alert('Desculpe! Não poderemos atende-lo.')</script>";
}

A new admin, you say? | Forum Promotion

Normally we just utilize the Staff Promotions topic, but why not? We have a new Administrator on board. We call her Kathy, but you probably know her as Lammy or @Lämmchen. Kathy has been our CTL for a little bit over a year now, and she’s exceeded every expectation that we had of her. She is a natural leader who is honest and unbias no matter what the circumstances may be. This is why she is perfect for this role.

50Th Anniversary Happy Dance GIF by Sesame Street

Please join me in congratulating her on joining the blue crew!

admin – 2FA preference not saving when multiple 2FA providers selected

I have recently enabled U2F for admin users. Previously, Authy was the only enabled provider. When U2F was enabled, all admin users were forced to re-configure their Two-Factor Authorization.

If the user has access to a U2F key, there is no problem. If the user does not have one, they are asked prompted to configure their Two-Factor Authorization every time they log in. After re-configuring, they are directed to the admin dashboard as expected. Once they log out and need to log in again, they are once again prompted to configure 2FA.

Has any one else experienced this issue? Some users would prefer to use their U2F keys but not everyone has access to one, so I can’t expect them to re-configure every time they need to login.

This is on Magento 2.4.2

Redirect users from admin pages the optimal solution

On two different sites I use two somewhat different solutions. Are they different in terms of security and performance? Which is better? Are there even better ones?

Solution 1.

/** Redirect users from admin pages if not administrators **/
add_action( 'admin_init', function() {
    if( ! current_user_can( 'manage_options' ) && ( ! wp_doing_ajax() ) ) {
        wp_safe_redirect( site_url() );
        exit;
    }
} );

Solution 2.

/** Redirect users from admin pages if not administrators **/
add_action( 'admin_init', function() {
    if( ! in_array( 'administrator', wp_get_current_user()->roles ) ) {
        wp_redirect( get_bloginfo( 'wpurl' ) );
        exit;
    }
} );

For Admin Tech

This is my first package request. Please be kind if I make any errors :]

I would like to request Ruby Package for my forum : https://admintech.flarum.cloud/

Hi | Forum Promotion – Best Webmaster, Admin and Internet Marketing Forum

Bizdustry – Business & Economics

Bizdustry is a make money forum where members can talk about business, economics, finances, budgeting, investing and anything Crypto related. Members earn 0.01$ for every message they publish and 1$ for each member that is referred to the forum. We strive to provide the best quality content and…